]> git.lizzy.rs Git - dragonfireclient.git/blob - src/server.cpp
da643339bb5f4d61da7820bef6d425d4c329a4bc
[dragonfireclient.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 #include "materials.h"
34
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
36
37 void * ServerThread::Thread()
38 {
39         ThreadStarted();
40
41         DSTACK(__FUNCTION_NAME);
42
43         BEGIN_DEBUG_EXCEPTION_HANDLER
44
45         while(getRun())
46         {
47                 try{
48                         m_server->AsyncRunStep();
49                 
50                         //dout_server<<"Running m_server->Receive()"<<std::endl;
51                         m_server->Receive();
52                 }
53                 catch(con::NoIncomingDataException &e)
54                 {
55                 }
56         }
57         
58         END_DEBUG_EXCEPTION_HANDLER
59
60         return NULL;
61 }
62
63 void * EmergeThread::Thread()
64 {
65         ThreadStarted();
66
67         DSTACK(__FUNCTION_NAME);
68
69         bool debug=false;
70         
71         BEGIN_DEBUG_EXCEPTION_HANDLER
72
73         /*
74                 Get block info from queue, emerge them and send them
75                 to clients.
76
77                 After queue is empty, exit.
78         */
79         while(getRun())
80         {
81                 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
82                 if(qptr == NULL)
83                         break;
84                 
85                 SharedPtr<QueuedBlockEmerge> q(qptr);
86
87                 v3s16 &p = q->pos;
88                 
89                 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
90
91                 //TimeTaker timer("block emerge");
92                 
93                 /*
94                         Try to emerge it from somewhere.
95
96                         If it is only wanted as optional, only loading from disk
97                         will be allowed.
98                 */
99                 
100                 /*
101                         Check if any peer wants it as non-optional. In that case it
102                         will be generated.
103
104                         Also decrement the emerge queue count in clients.
105                 */
106
107                 bool optional = true;
108
109                 {
110                         core::map<u16, u8>::Iterator i;
111                         for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
112                         {
113                                 //u16 peer_id = i.getNode()->getKey();
114
115                                 // Check flags
116                                 u8 flags = i.getNode()->getValue();
117                                 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
118                                         optional = false;
119                                 
120                         }
121                 }
122
123                 /*dstream<<"EmergeThread: p="
124                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
125                                 <<"optional="<<optional<<std::endl;*/
126                 
127                 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
128                         
129                 core::map<v3s16, MapBlock*> changed_blocks;
130                 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
131
132                 MapBlock *block = NULL;
133                 bool got_block = true;
134                 core::map<v3s16, MapBlock*> modified_blocks;
135                 
136                 {//envlock
137
138                 //TimeTaker envlockwaittimer("block emerge envlock wait time");
139                 
140                 // 0-50ms
141                 JMutexAutoLock envlock(m_server->m_env_mutex);
142
143                 //envlockwaittimer.stop();
144
145                 //TimeTaker timer("block emerge (while env locked)");
146                         
147                 try{
148                         bool only_from_disk = false;
149                         
150                         if(optional)
151                                 only_from_disk = true;
152                         
153                         // First check if the block already exists
154                         if(only_from_disk)
155                         {
156                                 block = map.getBlockNoCreate(p);
157                         }
158
159                         if(block == NULL)
160                         {
161                                 block = map.emergeBlock(
162                                                 p,
163                                                 only_from_disk,
164                                                 changed_blocks,
165                                                 lighting_invalidated_blocks);
166                         
167                                 /*
168                                         EXPERIMENTAL: Create a few other blocks too
169                                 */
170                                 
171                                 map.emergeBlock(
172                                                 p + v3s16(0,1,0),
173                                                 only_from_disk,
174                                                 changed_blocks,
175                                                 lighting_invalidated_blocks);
176
177                                 map.emergeBlock(
178                                                 p + v3s16(0,-1,0),
179                                                 only_from_disk,
180                                                 changed_blocks,
181                                                 lighting_invalidated_blocks);
182                                 
183                         }
184
185                         // If it is a dummy, block was not found on disk
186                         if(block->isDummy())
187                         {
188                                 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
189                                 got_block = false;
190                         }
191                 }
192                 catch(InvalidPositionException &e)
193                 {
194                         // Block not found.
195                         // This happens when position is over limit.
196                         got_block = false;
197                 }
198                 
199                 if(got_block)
200                 {
201                         if(debug && changed_blocks.size() > 0)
202                         {
203                                 dout_server<<DTIME<<"Got changed_blocks: ";
204                                 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
205                                                 i.atEnd() == false; i++)
206                                 {
207                                         MapBlock *block = i.getNode()->getValue();
208                                         v3s16 p = block->getPos();
209                                         dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
210                                 }
211                                 dout_server<<std::endl;
212                         }
213
214                         /*
215                                 Update water pressure
216                         */
217
218                         m_server->UpdateBlockWaterPressure(block, modified_blocks);
219
220                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
221                                         i.atEnd() == false; i++)
222                         {
223                                 MapBlock *block = i.getNode()->getValue();
224                                 m_server->UpdateBlockWaterPressure(block, modified_blocks);
225                                 //v3s16 p = i.getNode()->getKey();
226                                 //m_server->UpdateBlockWaterPressure(p, modified_blocks);
227                         }
228
229                         /*
230                                 Collect a list of blocks that have been modified in
231                                 addition to the fetched one.
232                         */
233
234                         // Add all the "changed blocks" to modified_blocks
235                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
236                                         i.atEnd() == false; i++)
237                         {
238                                 MapBlock *block = i.getNode()->getValue();
239                                 modified_blocks.insert(block->getPos(), block);
240                         }
241                         
242                         /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
243                                         <<" blocks"<<std::endl;*/
244                         
245                         //TimeTaker timer("** updateLighting", g_device);
246                         
247                         // Update lighting without locking the environment mutex,
248                         // add modified blocks to changed blocks
249                         map.updateLighting(lighting_invalidated_blocks, modified_blocks);
250                 }
251                 // If we got no block, there should be no invalidated blocks
252                 else
253                 {
254                         assert(lighting_invalidated_blocks.size() == 0);
255                 }
256
257                 }//envlock
258
259                 /*
260                         Set sent status of modified blocks on clients
261                 */
262         
263                 // NOTE: Server's clients are also behind the connection mutex
264                 JMutexAutoLock lock(m_server->m_con_mutex);
265
266                 /*
267                         Add the originally fetched block to the modified list
268                 */
269                 if(got_block)
270                 {
271                         modified_blocks.insert(p, block);
272                 }
273                 
274                 /*
275                         Set the modified blocks unsent for all the clients
276                 */
277                 
278                 for(core::map<u16, RemoteClient*>::Iterator
279                                 i = m_server->m_clients.getIterator();
280                                 i.atEnd() == false; i++)
281                 {
282                         RemoteClient *client = i.getNode()->getValue();
283                         
284                         if(modified_blocks.size() > 0)
285                         {
286                                 // Remove block from sent history
287                                 client->SetBlocksNotSent(modified_blocks);
288                         }
289                 }
290                 
291         }
292
293         END_DEBUG_EXCEPTION_HANDLER
294
295         return NULL;
296 }
297
298 void RemoteClient::GetNextBlocks(Server *server, float dtime,
299                 core::array<PrioritySortedBlockTransfer> &dest)
300 {
301         DSTACK(__FUNCTION_NAME);
302         
303         // Increment timers
304         {
305                 JMutexAutoLock lock(m_blocks_sent_mutex);
306                 m_nearest_unsent_reset_timer += dtime;
307         }
308
309         // Won't send anything if already sending
310         {
311                 JMutexAutoLock lock(m_blocks_sending_mutex);
312                 
313                 if(m_blocks_sending.size() >= g_settings.getU16
314                                 ("max_simultaneous_block_sends_per_client"))
315                 {
316                         //dstream<<"Not sending any blocks, Queue full."<<std::endl;
317                         return;
318                 }
319         }
320
321         Player *player = server->m_env.getPlayer(peer_id);
322
323         assert(player != NULL);
324
325         v3f playerpos = player->getPosition();
326         v3f playerspeed = player->getSpeed();
327
328         v3s16 center_nodepos = floatToInt(playerpos);
329
330         v3s16 center = getNodeBlockPos(center_nodepos);
331
332         /*
333                 Get the starting value of the block finder radius.
334         */
335         s16 last_nearest_unsent_d;
336         s16 d_start;
337         {
338                 JMutexAutoLock lock(m_blocks_sent_mutex);
339                 
340                 if(m_last_center != center)
341                 {
342                         m_nearest_unsent_d = 0;
343                         m_last_center = center;
344                 }
345
346                 /*dstream<<"m_nearest_unsent_reset_timer="
347                                 <<m_nearest_unsent_reset_timer<<std::endl;*/
348                 if(m_nearest_unsent_reset_timer > 5.0)
349                 {
350                         m_nearest_unsent_reset_timer = 0;
351                         m_nearest_unsent_d = 0;
352                         //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
353                 }
354
355                 last_nearest_unsent_d = m_nearest_unsent_d;
356                 
357                 d_start = m_nearest_unsent_d;
358         }
359
360         u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
361                         ("max_simultaneous_block_sends_per_client");
362         u16 maximum_simultaneous_block_sends = 
363                         maximum_simultaneous_block_sends_setting;
364
365         /*
366                 Check the time from last addNode/removeNode.
367                 
368                 Decrease send rate if player is building stuff.
369         */
370         {
371                 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
372                 m_time_from_building.m_value += dtime;
373                 /*if(m_time_from_building.m_value
374                                 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
375                 if(m_time_from_building.m_value < g_settings.getFloat(
376                                         "full_block_send_enable_min_time_from_building"))
377                 {
378                         maximum_simultaneous_block_sends
379                                 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
380                 }
381         }
382         
383         u32 num_blocks_selected;
384         {
385                 JMutexAutoLock lock(m_blocks_sending_mutex);
386                 num_blocks_selected = m_blocks_sending.size();
387         }
388         
389         /*
390                 next time d will be continued from the d from which the nearest
391                 unsent block was found this time.
392
393                 This is because not necessarily any of the blocks found this
394                 time are actually sent.
395         */
396         s32 new_nearest_unsent_d = -1;
397
398         // Serialization version used
399         //u8 ser_version = serialization_version;
400
401         //bool has_incomplete_blocks = false;
402         
403         s16 d_max = g_settings.getS16("max_block_send_distance");
404         s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
405         
406         //dstream<<"Starting from "<<d_start<<std::endl;
407
408         for(s16 d = d_start; d <= d_max; d++)
409         {
410                 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
411                 
412                 //if(has_incomplete_blocks == false)
413                 {
414                         JMutexAutoLock lock(m_blocks_sent_mutex);
415                         /*
416                                 If m_nearest_unsent_d was changed by the EmergeThread
417                                 (it can change it to 0 through SetBlockNotSent),
418                                 update our d to it.
419                                 Else update m_nearest_unsent_d
420                         */
421                         if(m_nearest_unsent_d != last_nearest_unsent_d)
422                         {
423                                 d = m_nearest_unsent_d;
424                                 last_nearest_unsent_d = m_nearest_unsent_d;
425                         }
426                 }
427
428                 /*
429                         Get the border/face dot coordinates of a "d-radiused"
430                         box
431                 */
432                 core::list<v3s16> list;
433                 getFacePositions(list, d);
434                 
435                 core::list<v3s16>::Iterator li;
436                 for(li=list.begin(); li!=list.end(); li++)
437                 {
438                         v3s16 p = *li + center;
439                         
440                         /*
441                                 Send throttling
442                                 - Don't allow too many simultaneous transfers
443                                 - EXCEPT when the blocks are very close
444
445                                 Also, don't send blocks that are already flying.
446                         */
447                         
448                         u16 maximum_simultaneous_block_sends_now =
449                                         maximum_simultaneous_block_sends;
450                         
451                         if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
452                         {
453                                 maximum_simultaneous_block_sends_now =
454                                                 maximum_simultaneous_block_sends_setting;
455                         }
456
457                         {
458                                 JMutexAutoLock lock(m_blocks_sending_mutex);
459                                 
460                                 // Limit is dynamically lowered when building
461                                 if(num_blocks_selected
462                                                 >= maximum_simultaneous_block_sends_now)
463                                 {
464                                         /*dstream<<"Not sending more blocks. Queue full. "
465                                                         <<m_blocks_sending.size()
466                                                         <<std::endl;*/
467                                         goto queue_full;
468                                 }
469
470                                 if(m_blocks_sending.find(p) != NULL)
471                                         continue;
472                         }
473                         
474                         /*
475                                 Do not go over-limit
476                         */
477                         if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
478                         || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
479                         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
480                         || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
481                         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
482                         || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
483                                 continue;
484
485                         bool generate = d <= d_max_gen;
486                         
487                         if(HAXMODE)
488                         {
489                                 // Don't generate above player
490                                 if(p.Y > center.Y)
491                                         generate = false;
492                         }
493                         else
494                         {
495                                 // Limit the generating area vertically to 2/3
496                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
497                                         generate = false;
498                         }
499                         
500                         /*
501                                 Don't send already sent blocks
502                         */
503                         {
504                                 JMutexAutoLock lock(m_blocks_sent_mutex);
505                                 
506                                 if(m_blocks_sent.find(p) != NULL)
507                                         continue;
508                         }
509
510                         if(HAXMODE)
511                         {
512                                 /*
513                                         Ignore block if it is not at ground surface
514                                 */
515                                 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
516                                                 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
517                                 f32 y = server->m_env.getMap().getGroundHeight(p2d);
518                                 // The sector might not exist yet, thus no heightmap
519                                 if(y > GROUNDHEIGHT_VALID_MINVALUE)
520                                 {
521                                         f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
522                                         if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3)
523                                                 continue;
524                                 }
525                         }
526
527                         /*
528                                 Check if map has this block
529                         */
530                         MapBlock *block = NULL;
531                         try
532                         {
533                                 block = server->m_env.getMap().getBlockNoCreate(p);
534                         }
535                         catch(InvalidPositionException &e)
536                         {
537                         }
538                         
539                         bool surely_not_found_on_disk = false;
540                         if(block != NULL)
541                         {
542                                 /*if(block->isIncomplete())
543                                 {
544                                         has_incomplete_blocks = true;
545                                         continue;
546                                 }*/
547
548                                 if(block->isDummy())
549                                 {
550                                         surely_not_found_on_disk = true;
551                                 }
552                         }
553
554                         /*
555                                 If block has been marked to not exist on disk (dummy)
556                                 and generating new ones is not wanted, skip block.
557                         */
558                         if(generate == false && surely_not_found_on_disk == true)
559                         {
560                                 // get next one.
561                                 continue;
562                         }
563
564                         /*
565                                 Record the lowest d from which a a block has been
566                                 found being not sent and possibly to exist
567                         */
568                         if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
569                         {
570                                 new_nearest_unsent_d = d;
571                         }
572                                         
573                         /*
574                                 Add inexistent block to emerge queue.
575                         */
576                         if(block == NULL || surely_not_found_on_disk)
577                         {
578                                 /*SharedPtr<JMutexAutoLock> lock
579                                                 (m_num_blocks_in_emerge_queue.getLock());*/
580                                 
581                                 //TODO: Get value from somewhere
582                                 // Allow only one block in emerge queue
583                                 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
584                                 {
585                                         // Add it to the emerge queue and trigger the thread
586                                         
587                                         u8 flags = 0;
588                                         if(generate == false)
589                                                 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
590                                         
591                                         server->m_emerge_queue.addBlock(peer_id, p, flags);
592                                         server->m_emergethread.trigger();
593                                 }
594                                 
595                                 // get next one.
596                                 continue;
597                         }
598
599                         /*
600                                 Add block to queue
601                         */
602
603                         PrioritySortedBlockTransfer q((float)d, p, peer_id);
604
605                         dest.push_back(q);
606
607                         num_blocks_selected += 1;
608                 }
609         }
610 queue_full:
611
612         if(new_nearest_unsent_d != -1)
613         {
614                 JMutexAutoLock lock(m_blocks_sent_mutex);
615                 m_nearest_unsent_d = new_nearest_unsent_d;
616         }
617 }
618
619 void RemoteClient::SendObjectData(
620                 Server *server,
621                 float dtime,
622                 core::map<v3s16, bool> &stepped_blocks
623         )
624 {
625         DSTACK(__FUNCTION_NAME);
626
627         // Can't send anything without knowing version
628         if(serialization_version == SER_FMT_VER_INVALID)
629         {
630                 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
631                                 <<std::endl;
632                 return;
633         }
634
635         /*
636                 Send a TOCLIENT_OBJECTDATA packet.
637                 Sent as unreliable.
638
639                 u16 command
640                 u16 number of player positions
641                 for each player:
642                         v3s32 position*100
643                         v3s32 speed*100
644                         s32 pitch*100
645                         s32 yaw*100
646                 u16 count of blocks
647                 for each block:
648                         block objects
649         */
650
651         std::ostringstream os(std::ios_base::binary);
652         u8 buf[12];
653         
654         // Write command
655         writeU16(buf, TOCLIENT_OBJECTDATA);
656         os.write((char*)buf, 2);
657         
658         /*
659                 Get and write player data
660         */
661         
662         // Get connected players
663         core::list<Player*> players = server->m_env.getPlayers(true);
664
665         // Write player count
666         u16 playercount = players.size();
667         writeU16(buf, playercount);
668         os.write((char*)buf, 2);
669
670         core::list<Player*>::Iterator i;
671         for(i = players.begin();
672                         i != players.end(); i++)
673         {
674                 Player *player = *i;
675
676                 v3f pf = player->getPosition();
677                 v3f sf = player->getSpeed();
678
679                 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
680                 v3s32 speed_i   (sf.X*100, sf.Y*100, sf.Z*100);
681                 s32   pitch_i   (player->getPitch() * 100);
682                 s32   yaw_i     (player->getYaw() * 100);
683                 
684                 writeU16(buf, player->peer_id);
685                 os.write((char*)buf, 2);
686                 writeV3S32(buf, position_i);
687                 os.write((char*)buf, 12);
688                 writeV3S32(buf, speed_i);
689                 os.write((char*)buf, 12);
690                 writeS32(buf, pitch_i);
691                 os.write((char*)buf, 4);
692                 writeS32(buf, yaw_i);
693                 os.write((char*)buf, 4);
694         }
695         
696         /*
697                 Get and write object data
698         */
699
700         /*
701                 Get nearby blocks.
702                 
703                 For making players to be able to build to their nearby
704                 environment (building is not possible on blocks that are not
705                 in memory):
706                 - Set blocks changed
707                 - Add blocks to emerge queue if they are not found
708
709                 SUGGESTION: These could be ignored from the backside of the player
710         */
711
712         Player *player = server->m_env.getPlayer(peer_id);
713
714         assert(player);
715
716         v3f playerpos = player->getPosition();
717         v3f playerspeed = player->getSpeed();
718
719         v3s16 center_nodepos = floatToInt(playerpos);
720         v3s16 center = getNodeBlockPos(center_nodepos);
721
722         s16 d_max = g_settings.getS16("active_object_range");
723         
724         // Number of blocks whose objects were written to bos
725         u16 blockcount = 0;
726
727         std::ostringstream bos(std::ios_base::binary);
728
729         for(s16 d = 0; d <= d_max; d++)
730         {
731                 core::list<v3s16> list;
732                 getFacePositions(list, d);
733                 
734                 core::list<v3s16>::Iterator li;
735                 for(li=list.begin(); li!=list.end(); li++)
736                 {
737                         v3s16 p = *li + center;
738
739                         /*
740                                 Ignore blocks that haven't been sent to the client
741                         */
742                         {
743                                 JMutexAutoLock sentlock(m_blocks_sent_mutex);
744                                 if(m_blocks_sent.find(p) == NULL)
745                                         continue;
746                         }
747                         
748                         // Try stepping block and add it to a send queue
749                         try
750                         {
751
752                         // Get block
753                         MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
754
755                         /*
756                                 Step block if not in stepped_blocks and add to stepped_blocks.
757                         */
758                         if(stepped_blocks.find(p) == NULL)
759                         {
760                                 block->stepObjects(dtime, true, server->getDayNightRatio());
761                                 stepped_blocks.insert(p, true);
762                                 block->setChangedFlag();
763                         }
764
765                         // Skip block if there are no objects
766                         if(block->getObjectCount() == 0)
767                                 continue;
768                         
769                         /*
770                                 Write objects
771                         */
772
773                         // Write blockpos
774                         writeV3S16(buf, p);
775                         bos.write((char*)buf, 6);
776
777                         // Write objects
778                         block->serializeObjects(bos, serialization_version);
779
780                         blockcount++;
781
782                         /*
783                                 Stop collecting objects if data is already too big
784                         */
785                         // Sum of player and object data sizes
786                         s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
787                         // break out if data too big
788                         if(sum > MAX_OBJECTDATA_SIZE)
789                         {
790                                 goto skip_subsequent;
791                         }
792                         
793                         } //try
794                         catch(InvalidPositionException &e)
795                         {
796                                 // Not in memory
797                                 // Add it to the emerge queue and trigger the thread.
798                                 // Fetch the block only if it is on disk.
799                                 
800                                 // Grab and increment counter
801                                 /*SharedPtr<JMutexAutoLock> lock
802                                                 (m_num_blocks_in_emerge_queue.getLock());
803                                 m_num_blocks_in_emerge_queue.m_value++;*/
804                                 
805                                 // Add to queue as an anonymous fetch from disk
806                                 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
807                                 server->m_emerge_queue.addBlock(0, p, flags);
808                                 server->m_emergethread.trigger();
809                         }
810                 }
811         }
812
813 skip_subsequent:
814
815         // Write block count
816         writeU16(buf, blockcount);
817         os.write((char*)buf, 2);
818
819         // Write block objects
820         os<<bos.str();
821
822         /*
823                 Send data
824         */
825         
826         //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
827
828         // Make data buffer
829         std::string s = os.str();
830         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
831         // Send as unreliable
832         server->m_con.Send(peer_id, 0, data, false);
833 }
834
835 void RemoteClient::GotBlock(v3s16 p)
836 {
837         JMutexAutoLock lock(m_blocks_sending_mutex);
838         JMutexAutoLock lock2(m_blocks_sent_mutex);
839         if(m_blocks_sending.find(p) != NULL)
840                 m_blocks_sending.remove(p);
841         else
842                 dstream<<"RemoteClient::GotBlock(): Didn't find in"
843                                 " m_blocks_sending"<<std::endl;
844         m_blocks_sent.insert(p, true);
845 }
846
847 void RemoteClient::SentBlock(v3s16 p)
848 {
849         JMutexAutoLock lock(m_blocks_sending_mutex);
850         if(m_blocks_sending.size() > 15)
851         {
852                 dstream<<"RemoteClient::SentBlock(): "
853                                 <<"m_blocks_sending.size()="
854                                 <<m_blocks_sending.size()<<std::endl;
855         }
856         if(m_blocks_sending.find(p) == NULL)
857                 m_blocks_sending.insert(p, 0.0);
858         else
859                 dstream<<"RemoteClient::SentBlock(): Sent block"
860                                 " already in m_blocks_sending"<<std::endl;
861 }
862
863 void RemoteClient::SetBlockNotSent(v3s16 p)
864 {
865         JMutexAutoLock sendinglock(m_blocks_sending_mutex);
866         JMutexAutoLock sentlock(m_blocks_sent_mutex);
867
868         m_nearest_unsent_d = 0;
869         
870         if(m_blocks_sending.find(p) != NULL)
871                 m_blocks_sending.remove(p);
872         if(m_blocks_sent.find(p) != NULL)
873                 m_blocks_sent.remove(p);
874 }
875
876 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
877 {
878         JMutexAutoLock sendinglock(m_blocks_sending_mutex);
879         JMutexAutoLock sentlock(m_blocks_sent_mutex);
880
881         m_nearest_unsent_d = 0;
882         
883         for(core::map<v3s16, MapBlock*>::Iterator
884                         i = blocks.getIterator();
885                         i.atEnd()==false; i++)
886         {
887                 v3s16 p = i.getNode()->getKey();
888
889                 if(m_blocks_sending.find(p) != NULL)
890                         m_blocks_sending.remove(p);
891                 if(m_blocks_sent.find(p) != NULL)
892                         m_blocks_sent.remove(p);
893         }
894 }
895
896 /*
897         PlayerInfo
898 */
899
900 PlayerInfo::PlayerInfo()
901 {
902         name[0] = 0;
903 }
904
905 void PlayerInfo::PrintLine(std::ostream *s)
906 {
907         (*s)<<id<<": ";
908         (*s)<<"\""<<name<<"\" ("
909                         <<position.X<<","<<position.Y
910                         <<","<<position.Z<<") ";
911         address.print(s);
912         (*s)<<" avg_rtt="<<avg_rtt;
913         (*s)<<std::endl;
914 }
915
916 u32 PIChecksum(core::list<PlayerInfo> &l)
917 {
918         core::list<PlayerInfo>::Iterator i;
919         u32 checksum = 1;
920         u32 a = 10;
921         for(i=l.begin(); i!=l.end(); i++)
922         {
923                 checksum += a * (i->id+1);
924                 checksum ^= 0x435aafcd;
925                 a *= 10;
926         }
927         return checksum;
928 }
929
930 /*
931         Server
932 */
933
934 Server::Server(
935                 std::string mapsavedir,
936                 HMParams hm_params,
937                 MapParams map_params
938         ):
939         m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
940         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
941         m_thread(this),
942         m_emergethread(this),
943         m_time_of_day(9000),
944         m_time_counter(0),
945         m_time_of_day_send_timer(0),
946         m_uptime(0)
947 {
948         m_flowwater_timer = 0.0;
949         m_print_info_timer = 0.0;
950         m_objectdata_timer = 0.0;
951         m_emergethread_trigger_timer = 0.0;
952         m_savemap_timer = 0.0;
953         
954         m_env_mutex.Init();
955         m_con_mutex.Init();
956         m_step_dtime_mutex.Init();
957         m_step_dtime = 0.0;
958 }
959
960 Server::~Server()
961 {
962         // Stop threads
963         stop();
964
965         JMutexAutoLock clientslock(m_con_mutex);
966
967         for(core::map<u16, RemoteClient*>::Iterator
968                 i = m_clients.getIterator();
969                 i.atEnd() == false; i++)
970         {
971                 /*// Delete player
972                 // NOTE: These are removed by env destructor
973                 {
974                         u16 peer_id = i.getNode()->getKey();
975                         JMutexAutoLock envlock(m_env_mutex);
976                         m_env.removePlayer(peer_id);
977                 }*/
978                 
979                 // Delete client
980                 delete i.getNode()->getValue();
981         }
982 }
983
984 void Server::start(unsigned short port)
985 {
986         DSTACK(__FUNCTION_NAME);
987         // Stop thread if already running
988         m_thread.stop();
989         
990         // Initialize connection
991         m_con.setTimeoutMs(30);
992         m_con.Serve(port);
993
994         // Start thread
995         m_thread.setRun(true);
996         m_thread.Start();
997         
998         dout_server<<"Server started on port "<<port<<std::endl;
999 }
1000
1001 void Server::stop()
1002 {
1003         DSTACK(__FUNCTION_NAME);
1004         // Stop threads (set run=false first so both start stopping)
1005         m_thread.setRun(false);
1006         m_emergethread.setRun(false);
1007         m_thread.stop();
1008         m_emergethread.stop();
1009         
1010         dout_server<<"Server threads stopped"<<std::endl;
1011 }
1012
1013 void Server::step(float dtime)
1014 {
1015         DSTACK(__FUNCTION_NAME);
1016         // Limit a bit
1017         if(dtime > 2.0)
1018                 dtime = 2.0;
1019         {
1020                 JMutexAutoLock lock(m_step_dtime_mutex);
1021                 m_step_dtime += dtime;
1022         }
1023 }
1024
1025 void Server::AsyncRunStep()
1026 {
1027         DSTACK(__FUNCTION_NAME);
1028         
1029         float dtime;
1030         {
1031                 JMutexAutoLock lock1(m_step_dtime_mutex);
1032                 dtime = m_step_dtime;
1033         }
1034         
1035         // Send blocks to clients
1036         SendBlocks(dtime);
1037         
1038         if(dtime < 0.001)
1039                 return;
1040         
1041         //dstream<<"Server steps "<<dtime<<std::endl;
1042         //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1043         
1044         {
1045                 JMutexAutoLock lock1(m_step_dtime_mutex);
1046                 m_step_dtime -= dtime;
1047         }
1048
1049         /*
1050                 Update uptime
1051         */
1052         {
1053                 m_uptime.set(m_uptime.get() + dtime);
1054         }
1055         
1056         /*
1057                 Update m_time_of_day
1058         */
1059         {
1060                 m_time_counter += dtime;
1061                 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1062                 u32 units = (u32)(m_time_counter*speed);
1063                 m_time_counter -= (f32)units / speed;
1064                 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1065                 
1066                 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1067
1068                 /*
1069                         Send to clients at constant intervals
1070                 */
1071
1072                 m_time_of_day_send_timer -= dtime;
1073                 if(m_time_of_day_send_timer < 0.0)
1074                 {
1075                         m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1076
1077                         //JMutexAutoLock envlock(m_env_mutex);
1078                         JMutexAutoLock conlock(m_con_mutex);
1079
1080                         for(core::map<u16, RemoteClient*>::Iterator
1081                                 i = m_clients.getIterator();
1082                                 i.atEnd() == false; i++)
1083                         {
1084                                 RemoteClient *client = i.getNode()->getValue();
1085                                 //Player *player = m_env.getPlayer(client->peer_id);
1086                                 
1087                                 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1088                                                 m_time_of_day.get());
1089                                 // Send as reliable
1090                                 m_con.Send(client->peer_id, 0, data, true);
1091                         }
1092                 }
1093         }
1094
1095         {
1096                 // Process connection's timeouts
1097                 JMutexAutoLock lock2(m_con_mutex);
1098                 m_con.RunTimeouts(dtime);
1099         }
1100         
1101         {
1102                 // This has to be called so that the client list gets synced
1103                 // with the peer list of the connection
1104                 handlePeerChanges();
1105         }
1106
1107         {
1108                 // Step environment
1109                 // This also runs Map's timers
1110                 JMutexAutoLock lock(m_env_mutex);
1111                 m_env.step(dtime);
1112         }
1113         
1114         /*
1115                 Do background stuff
1116         */
1117
1118         /*
1119                 Flow water
1120         */
1121         {
1122                 float interval;
1123                 
1124                 if(g_settings.getBool("endless_water") == false)
1125                         interval = 1.0;
1126                 else
1127                         interval = 0.25;
1128
1129                 float &counter = m_flowwater_timer;
1130                 counter += dtime;
1131                 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1132                 {
1133                 
1134                 counter = 0.0;
1135
1136                 core::map<v3s16, MapBlock*> modified_blocks;
1137
1138                 {
1139
1140                         JMutexAutoLock envlock(m_env_mutex);
1141                         
1142                         MapVoxelManipulator v(&m_env.getMap());
1143                         v.m_disable_water_climb =
1144                                         g_settings.getBool("disable_water_climb");
1145                         
1146                         if(g_settings.getBool("endless_water") == false)
1147                                 v.flowWater(m_flow_active_nodes, 0, false, 250);
1148                         else
1149                                 v.flowWater(m_flow_active_nodes, 0, false, 50);
1150
1151                         v.blitBack(modified_blocks);
1152
1153                         ServerMap &map = ((ServerMap&)m_env.getMap());
1154                         
1155                         // Update lighting
1156                         core::map<v3s16, MapBlock*> lighting_modified_blocks;
1157                         map.updateLighting(modified_blocks, lighting_modified_blocks);
1158                         
1159                         // Add blocks modified by lighting to modified_blocks
1160                         for(core::map<v3s16, MapBlock*>::Iterator
1161                                         i = lighting_modified_blocks.getIterator();
1162                                         i.atEnd() == false; i++)
1163                         {
1164                                 MapBlock *block = i.getNode()->getValue();
1165                                 modified_blocks.insert(block->getPos(), block);
1166                         }
1167                 } // envlock
1168
1169                 /*
1170                         Set the modified blocks unsent for all the clients
1171                 */
1172                 
1173                 JMutexAutoLock lock2(m_con_mutex);
1174
1175                 for(core::map<u16, RemoteClient*>::Iterator
1176                                 i = m_clients.getIterator();
1177                                 i.atEnd() == false; i++)
1178                 {
1179                         RemoteClient *client = i.getNode()->getValue();
1180                         
1181                         if(modified_blocks.size() > 0)
1182                         {
1183                                 // Remove block from sent history
1184                                 client->SetBlocksNotSent(modified_blocks);
1185                         }
1186                 }
1187
1188                 } // interval counter
1189         }
1190         
1191         // Periodically print some info
1192         {
1193                 float &counter = m_print_info_timer;
1194                 counter += dtime;
1195                 if(counter >= 30.0)
1196                 {
1197                         counter = 0.0;
1198
1199                         JMutexAutoLock lock2(m_con_mutex);
1200
1201                         for(core::map<u16, RemoteClient*>::Iterator
1202                                 i = m_clients.getIterator();
1203                                 i.atEnd() == false; i++)
1204                         {
1205                                 //u16 peer_id = i.getNode()->getKey();
1206                                 RemoteClient *client = i.getNode()->getValue();
1207                                 client->PrintInfo(std::cout);
1208                         }
1209                 }
1210         }
1211
1212         /*
1213                 Update digging
1214
1215                 NOTE: Some of this could be moved to RemoteClient
1216         */
1217 #if 0
1218         {
1219                 JMutexAutoLock envlock(m_env_mutex);
1220                 JMutexAutoLock conlock(m_con_mutex);
1221
1222                 for(core::map<u16, RemoteClient*>::Iterator
1223                         i = m_clients.getIterator();
1224                         i.atEnd() == false; i++)
1225                 {
1226                         RemoteClient *client = i.getNode()->getValue();
1227                         Player *player = m_env.getPlayer(client->peer_id);
1228
1229                         JMutexAutoLock digmutex(client->m_dig_mutex);
1230
1231                         if(client->m_dig_tool_item == -1)
1232                                 continue;
1233
1234                         client->m_dig_time_remaining -= dtime;
1235
1236                         if(client->m_dig_time_remaining > 0)
1237                         {
1238                                 client->m_time_from_building.set(0.0);
1239                                 continue;
1240                         }
1241
1242                         v3s16 p_under = client->m_dig_position;
1243                         
1244                         // Mandatory parameter; actually used for nothing
1245                         core::map<v3s16, MapBlock*> modified_blocks;
1246
1247                         u8 material;
1248
1249                         try
1250                         {
1251                                 // Get material at position
1252                                 material = m_env.getMap().getNode(p_under).d;
1253                                 // If it's not diggable, do nothing
1254                                 if(content_diggable(material) == false)
1255                                 {
1256                                         derr_server<<"Server: Not finishing digging: Node not diggable"
1257                                                         <<std::endl;
1258                                         client->m_dig_tool_item = -1;
1259                                         break;
1260                                 }
1261                         }
1262                         catch(InvalidPositionException &e)
1263                         {
1264                                 derr_server<<"Server: Not finishing digging: Node not found"
1265                                                 <<std::endl;
1266                                 client->m_dig_tool_item = -1;
1267                                 break;
1268                         }
1269                         
1270                         // Create packet
1271                         u32 replysize = 8;
1272                         SharedBuffer<u8> reply(replysize);
1273                         writeU16(&reply[0], TOCLIENT_REMOVENODE);
1274                         writeS16(&reply[2], p_under.X);
1275                         writeS16(&reply[4], p_under.Y);
1276                         writeS16(&reply[6], p_under.Z);
1277                         // Send as reliable
1278                         m_con.SendToAll(0, reply, true);
1279                         
1280                         if(g_settings.getBool("creative_mode") == false)
1281                         {
1282                                 // Add to inventory and send inventory
1283                                 InventoryItem *item = new MaterialItem(material, 1);
1284                                 player->inventory.addItem("main", item);
1285                                 SendInventory(player->peer_id);
1286                         }
1287
1288                         /*
1289                                 Remove the node
1290                                 (this takes some time so it is done after the quick stuff)
1291                         */
1292                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1293                         
1294                         /*
1295                                 Update water
1296                         */
1297                         
1298                         // Update water pressure around modification
1299                         // This also adds it to m_flow_active_nodes if appropriate
1300
1301                         MapVoxelManipulator v(&m_env.getMap());
1302                         v.m_disable_water_climb =
1303                                         g_settings.getBool("disable_water_climb");
1304                         
1305                         VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1306
1307                         try
1308                         {
1309                                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1310                         }
1311                         catch(ProcessingLimitException &e)
1312                         {
1313                                 dstream<<"Processing limit reached (1)"<<std::endl;
1314                         }
1315                         
1316                         v.blitBack(modified_blocks);
1317                 }
1318         }
1319 #endif
1320
1321         // Send object positions
1322         {
1323                 float &counter = m_objectdata_timer;
1324                 counter += dtime;
1325                 if(counter >= g_settings.getFloat("objectdata_interval"))
1326                 {
1327                         JMutexAutoLock lock1(m_env_mutex);
1328                         JMutexAutoLock lock2(m_con_mutex);
1329                         SendObjectData(counter);
1330
1331                         counter = 0.0;
1332                 }
1333         }
1334         
1335         // Trigger emergethread (it gets somehow gets to a
1336         // non-triggered but bysy state sometimes)
1337         {
1338                 float &counter = m_emergethread_trigger_timer;
1339                 counter += dtime;
1340                 if(counter >= 2.0)
1341                 {
1342                         counter = 0.0;
1343                         
1344                         m_emergethread.trigger();
1345                 }
1346         }
1347
1348         // Save map
1349         {
1350                 float &counter = m_savemap_timer;
1351                 counter += dtime;
1352                 if(counter >= g_settings.getFloat("server_map_save_interval"))
1353                 {
1354                         counter = 0.0;
1355
1356                         JMutexAutoLock lock(m_env_mutex);
1357
1358                         // Save only changed parts
1359                         m_env.getMap().save(true);
1360
1361                         // Delete unused sectors
1362                         u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1363                                         g_settings.getFloat("server_unload_unused_sectors_timeout"));
1364                         if(deleted_count > 0)
1365                         {
1366                                 dout_server<<"Server: Unloaded "<<deleted_count
1367                                                 <<" sectors from memory"<<std::endl;
1368                         }
1369                 }
1370         }
1371 }
1372
1373 void Server::Receive()
1374 {
1375         DSTACK(__FUNCTION_NAME);
1376         u32 data_maxsize = 10000;
1377         Buffer<u8> data(data_maxsize);
1378         u16 peer_id;
1379         u32 datasize;
1380         try{
1381                 {
1382                         JMutexAutoLock conlock(m_con_mutex);
1383                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1384                 }
1385
1386                 // This has to be called so that the client list gets synced
1387                 // with the peer list of the connection
1388                 handlePeerChanges();
1389
1390                 ProcessData(*data, datasize, peer_id);
1391         }
1392         catch(con::InvalidIncomingDataException &e)
1393         {
1394                 derr_server<<"Server::Receive(): "
1395                                 "InvalidIncomingDataException: what()="
1396                                 <<e.what()<<std::endl;
1397         }
1398         catch(con::PeerNotFoundException &e)
1399         {
1400                 //NOTE: This is not needed anymore
1401                 
1402                 // The peer has been disconnected.
1403                 // Find the associated player and remove it.
1404
1405                 /*JMutexAutoLock envlock(m_env_mutex);
1406
1407                 dout_server<<"ServerThread: peer_id="<<peer_id
1408                                 <<" has apparently closed connection. "
1409                                 <<"Removing player."<<std::endl;
1410
1411                 m_env.removePlayer(peer_id);*/
1412         }
1413 }
1414
1415 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1416 {
1417         DSTACK(__FUNCTION_NAME);
1418         // Environment is locked first.
1419         JMutexAutoLock envlock(m_env_mutex);
1420         JMutexAutoLock conlock(m_con_mutex);
1421         
1422         con::Peer *peer;
1423         try{
1424                 peer = m_con.GetPeer(peer_id);
1425         }
1426         catch(con::PeerNotFoundException &e)
1427         {
1428                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1429                                 <<peer_id<<" not found"<<std::endl;
1430                 return;
1431         }
1432         
1433         //u8 peer_ser_ver = peer->serialization_version;
1434         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1435
1436         try
1437         {
1438
1439         if(datasize < 2)
1440                 return;
1441
1442         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1443         
1444         if(command == TOSERVER_INIT)
1445         {
1446                 // [0] u16 TOSERVER_INIT
1447                 // [2] u8 SER_FMT_VER_HIGHEST
1448                 // [3] u8[20] player_name
1449
1450                 if(datasize < 3)
1451                         return;
1452
1453                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1454                                 <<peer->id<<std::endl;
1455
1456                 // First byte after command is maximum supported
1457                 // serialization version
1458                 u8 client_max = data[2];
1459                 u8 our_max = SER_FMT_VER_HIGHEST;
1460                 // Use the highest version supported by both
1461                 u8 deployed = core::min_(client_max, our_max);
1462                 // If it's lower than the lowest supported, give up.
1463                 if(deployed < SER_FMT_VER_LOWEST)
1464                         deployed = SER_FMT_VER_INVALID;
1465
1466                 //peer->serialization_version = deployed;
1467                 getClient(peer->id)->pending_serialization_version = deployed;
1468
1469                 if(deployed == SER_FMT_VER_INVALID)
1470                 {
1471                         derr_server<<DTIME<<"Server: Cannot negotiate "
1472                                         "serialization version with peer "
1473                                         <<peer_id<<std::endl;
1474                         return;
1475                 }
1476
1477                 /*
1478                         Set up player
1479                 */
1480                 
1481                 // Get player name
1482                 const u32 playername_size = 20;
1483                 char playername[playername_size];
1484                 for(u32 i=0; i<playername_size-1; i++)
1485                 {
1486                         playername[i] = data[3+i];
1487                 }
1488                 playername[playername_size-1] = 0;
1489                 
1490                 // Get player
1491                 Player *player = emergePlayer(playername, "");
1492                 //Player *player = m_env.getPlayer(peer_id);
1493
1494                 // If a client is already connected to the player, cancel
1495                 if(player->peer_id != 0)
1496                 {
1497                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1498                                         <<" tried to connect to "
1499                                         "an already connected player (peer_id="
1500                                         <<player->peer_id<<")"<<std::endl;
1501                         return;
1502                 }
1503
1504                 // Set client of player
1505                 player->peer_id = peer_id;
1506
1507                 // Check if player doesn't exist
1508                 if(player == NULL)
1509                         throw con::InvalidIncomingDataException
1510                                 ("Server::ProcessData(): INIT: Player doesn't exist");
1511
1512                 /*// update name if it was supplied
1513                 if(datasize >= 20+3)
1514                 {
1515                         data[20+3-1] = 0;
1516                         player->updateName((const char*)&data[3]);
1517                 }*/
1518
1519                 // Now answer with a TOCLIENT_INIT
1520                 
1521                 SharedBuffer<u8> reply(2+1+6);
1522                 writeU16(&reply[0], TOCLIENT_INIT);
1523                 writeU8(&reply[2], deployed);
1524                 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1525                 // Send as reliable
1526                 m_con.Send(peer_id, 0, reply, true);
1527
1528                 return;
1529         }
1530         if(command == TOSERVER_INIT2)
1531         {
1532                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1533                                 <<peer->id<<std::endl;
1534
1535
1536                 getClient(peer->id)->serialization_version
1537                                 = getClient(peer->id)->pending_serialization_version;
1538
1539                 /*
1540                         Send some initialization data
1541                 */
1542                 
1543                 // Send player info to all players
1544                 SendPlayerInfos();
1545
1546                 // Send inventory to player
1547                 SendInventory(peer->id);
1548                 
1549                 // Send time of day
1550                 {
1551                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1552                                         m_time_of_day.get());
1553                         m_con.Send(peer->id, 0, data, true);
1554                 }
1555
1556                 // Send information about server to player in chat
1557                 {
1558                         std::wostringstream os(std::ios_base::binary);
1559                         os<<L"# Server: ";
1560                         // Uptime
1561                         os<<L"uptime="<<m_uptime.get();
1562                         // Information about clients
1563                         os<<L", clients={";
1564                         for(core::map<u16, RemoteClient*>::Iterator
1565                                 i = m_clients.getIterator();
1566                                 i.atEnd() == false; i++)
1567                         {
1568                                 // Get client and check that it is valid
1569                                 RemoteClient *client = i.getNode()->getValue();
1570                                 assert(client->peer_id == i.getNode()->getKey());
1571                                 if(client->serialization_version == SER_FMT_VER_INVALID)
1572                                         continue;
1573                                 // Get player
1574                                 Player *player = m_env.getPlayer(client->peer_id);
1575                                 // Get name of player
1576                                 std::wstring name = L"unknown";
1577                                 if(player != NULL)
1578                                         name = narrow_to_wide(player->getName());
1579                                 // Add name to information string
1580                                 os<<name<<L",";
1581                         }
1582                         os<<L"}";
1583                         // Send message
1584                         SendChatMessage(peer_id, os.str());
1585                 }
1586                 
1587                 // Send information about joining in chat
1588                 {
1589                         std::wstring name = L"unknown";
1590                         Player *player = m_env.getPlayer(peer_id);
1591                         if(player != NULL)
1592                                 name = narrow_to_wide(player->getName());
1593                         
1594                         std::wstring message;
1595                         message += L"*** ";
1596                         message += name;
1597                         message += L" joined game";
1598                         BroadcastChatMessage(message);
1599                 }
1600
1601                 return;
1602         }
1603
1604         if(peer_ser_ver == SER_FMT_VER_INVALID)
1605         {
1606                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1607                                 " serialization format invalid or not initialized."
1608                                 " Skipping incoming command="<<command<<std::endl;
1609                 return;
1610         }
1611         
1612         Player *player = m_env.getPlayer(peer_id);
1613
1614         if(player == NULL){
1615                 derr_server<<"Server::ProcessData(): Cancelling: "
1616                                 "No player for peer_id="<<peer_id
1617                                 <<std::endl;
1618                 return;
1619         }
1620         if(command == TOSERVER_PLAYERPOS)
1621         {
1622                 if(datasize < 2+12+12+4+4)
1623                         return;
1624         
1625                 u32 start = 0;
1626                 v3s32 ps = readV3S32(&data[start+2]);
1627                 v3s32 ss = readV3S32(&data[start+2+12]);
1628                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1629                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1630                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1631                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1632                 pitch = wrapDegrees(pitch);
1633                 yaw = wrapDegrees(yaw);
1634                 player->setPosition(position);
1635                 player->setSpeed(speed);
1636                 player->setPitch(pitch);
1637                 player->setYaw(yaw);
1638                 
1639                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1640                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1641                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1642         }
1643         else if(command == TOSERVER_GOTBLOCKS)
1644         {
1645                 if(datasize < 2+1)
1646                         return;
1647                 
1648                 /*
1649                         [0] u16 command
1650                         [2] u8 count
1651                         [3] v3s16 pos_0
1652                         [3+6] v3s16 pos_1
1653                         ...
1654                 */
1655
1656                 u16 count = data[2];
1657                 for(u16 i=0; i<count; i++)
1658                 {
1659                         if((s16)datasize < 2+1+(i+1)*6)
1660                                 throw con::InvalidIncomingDataException
1661                                         ("GOTBLOCKS length is too short");
1662                         v3s16 p = readV3S16(&data[2+1+i*6]);
1663                         /*dstream<<"Server: GOTBLOCKS ("
1664                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1665                         RemoteClient *client = getClient(peer_id);
1666                         client->GotBlock(p);
1667                 }
1668         }
1669         else if(command == TOSERVER_DELETEDBLOCKS)
1670         {
1671                 if(datasize < 2+1)
1672                         return;
1673                 
1674                 /*
1675                         [0] u16 command
1676                         [2] u8 count
1677                         [3] v3s16 pos_0
1678                         [3+6] v3s16 pos_1
1679                         ...
1680                 */
1681
1682                 u16 count = data[2];
1683                 for(u16 i=0; i<count; i++)
1684                 {
1685                         if((s16)datasize < 2+1+(i+1)*6)
1686                                 throw con::InvalidIncomingDataException
1687                                         ("DELETEDBLOCKS length is too short");
1688                         v3s16 p = readV3S16(&data[2+1+i*6]);
1689                         /*dstream<<"Server: DELETEDBLOCKS ("
1690                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1691                         RemoteClient *client = getClient(peer_id);
1692                         client->SetBlockNotSent(p);
1693                 }
1694         }
1695         else if(command == TOSERVER_CLICK_OBJECT)
1696         {
1697                 if(datasize < 13)
1698                         return;
1699
1700                 /*
1701                         [0] u16 command
1702                         [2] u8 button (0=left, 1=right)
1703                         [3] v3s16 block
1704                         [9] s16 id
1705                         [11] u16 item
1706                 */
1707                 u8 button = readU8(&data[2]);
1708                 v3s16 p;
1709                 p.X = readS16(&data[3]);
1710                 p.Y = readS16(&data[5]);
1711                 p.Z = readS16(&data[7]);
1712                 s16 id = readS16(&data[9]);
1713                 //u16 item_i = readU16(&data[11]);
1714
1715                 MapBlock *block = NULL;
1716                 try
1717                 {
1718                         block = m_env.getMap().getBlockNoCreate(p);
1719                 }
1720                 catch(InvalidPositionException &e)
1721                 {
1722                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1723                         return;
1724                 }
1725
1726                 MapBlockObject *obj = block->getObject(id);
1727
1728                 if(obj == NULL)
1729                 {
1730                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1731                         return;
1732                 }
1733
1734                 //TODO: Check that object is reasonably close
1735                 
1736                 // Left click
1737                 if(button == 0)
1738                 {
1739                         InventoryList *ilist = player->inventory.getList("main");
1740                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1741                         {
1742                         
1743                                 // Skip if inventory has no free space
1744                                 if(ilist->getUsedSlots() == ilist->getSize())
1745                                 {
1746                                         dout_server<<"Player inventory has no free space"<<std::endl;
1747                                         return;
1748                                 }
1749                                 
1750                                 /*
1751                                         Create the inventory item
1752                                 */
1753                                 InventoryItem *item = NULL;
1754                                 // If it is an item-object, take the item from it
1755                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1756                                 {
1757                                         item = ((ItemObject*)obj)->createInventoryItem();
1758                                 }
1759                                 // Else create an item of the object
1760                                 else
1761                                 {
1762                                         item = new MapBlockObjectItem
1763                                                         (obj->getInventoryString());
1764                                 }
1765                                 
1766                                 // Add to inventory and send inventory
1767                                 ilist->addItem(item);
1768                                 SendInventory(player->peer_id);
1769                         }
1770
1771                         // Remove from block
1772                         block->removeObject(id);
1773                 }
1774         }
1775         else if(command == TOSERVER_GROUND_ACTION)
1776         {
1777                 if(datasize < 17)
1778                         return;
1779                 /*
1780                         length: 17
1781                         [0] u16 command
1782                         [2] u8 action
1783                         [3] v3s16 nodepos_undersurface
1784                         [9] v3s16 nodepos_abovesurface
1785                         [15] u16 item
1786                         actions:
1787                         0: start digging
1788                         1: place block
1789                         2: stop digging (all parameters ignored)
1790                 */
1791                 u8 action = readU8(&data[2]);
1792                 v3s16 p_under;
1793                 p_under.X = readS16(&data[3]);
1794                 p_under.Y = readS16(&data[5]);
1795                 p_under.Z = readS16(&data[7]);
1796                 v3s16 p_over;
1797                 p_over.X = readS16(&data[9]);
1798                 p_over.Y = readS16(&data[11]);
1799                 p_over.Z = readS16(&data[13]);
1800                 u16 item_i = readU16(&data[15]);
1801
1802                 //TODO: Check that target is reasonably close
1803                 
1804                 /*
1805                         0: start digging
1806                 */
1807                 if(action == 0)
1808                 {
1809                         /*
1810                                 NOTE: This can be used in the future to check if
1811                                 somebody is cheating, by checking the timing.
1812                         */
1813                 } // action == 0
1814
1815                 /*
1816                         2: stop digging
1817                 */
1818                 else if(action == 2)
1819                 {
1820 #if 0
1821                         RemoteClient *client = getClient(peer->id);
1822                         JMutexAutoLock digmutex(client->m_dig_mutex);
1823                         client->m_dig_tool_item = -1;
1824 #endif
1825                 }
1826
1827                 /*
1828                         3: Digging completed
1829                 */
1830                 else if(action == 3)
1831                 {
1832                         // Mandatory parameter; actually used for nothing
1833                         core::map<v3s16, MapBlock*> modified_blocks;
1834
1835                         u8 material;
1836
1837                         try
1838                         {
1839                                 // Get material at position
1840                                 material = m_env.getMap().getNode(p_under).d;
1841                                 // If it's not diggable, do nothing
1842                                 if(content_diggable(material) == false)
1843                                 {
1844                                         derr_server<<"Server: Not finishing digging: Node not diggable"
1845                                                         <<std::endl;
1846                                         return;
1847                                 }
1848                         }
1849                         catch(InvalidPositionException &e)
1850                         {
1851                                 derr_server<<"Server: Not finishing digging: Node not found"
1852                                                 <<std::endl;
1853                                 return;
1854                         }
1855                         
1856                         //TODO: Send to only other clients
1857                         
1858                         /*
1859                                 Send the removal to all other clients
1860                         */
1861
1862                         // Create packet
1863                         u32 replysize = 8;
1864                         SharedBuffer<u8> reply(replysize);
1865                         writeU16(&reply[0], TOCLIENT_REMOVENODE);
1866                         writeS16(&reply[2], p_under.X);
1867                         writeS16(&reply[4], p_under.Y);
1868                         writeS16(&reply[6], p_under.Z);
1869
1870                         for(core::map<u16, RemoteClient*>::Iterator
1871                                 i = m_clients.getIterator();
1872                                 i.atEnd() == false; i++)
1873                         {
1874                                 // Get client and check that it is valid
1875                                 RemoteClient *client = i.getNode()->getValue();
1876                                 assert(client->peer_id == i.getNode()->getKey());
1877                                 if(client->serialization_version == SER_FMT_VER_INVALID)
1878                                         continue;
1879
1880                                 // Don't send if it's the same one
1881                                 if(peer_id == client->peer_id)
1882                                         continue;
1883
1884                                 // Send as reliable
1885                                 m_con.Send(client->peer_id, 0, reply, true);
1886                         }
1887                         
1888                         /*
1889                                 Update and send inventory
1890                         */
1891
1892                         if(g_settings.getBool("creative_mode") == false)
1893                         {
1894                                 /*
1895                                         Wear out tool
1896                                 */
1897                                 InventoryList *mlist = player->inventory.getList("main");
1898                                 if(mlist != NULL)
1899                                 {
1900                                         InventoryItem *item = mlist->getItem(item_i);
1901                                         if(item && (std::string)item->getName() == "ToolItem")
1902                                         {
1903                                                 ToolItem *titem = (ToolItem*)item;
1904                                                 std::string toolname = titem->getToolName();
1905
1906                                                 // Get digging properties for material and tool
1907                                                 DiggingProperties prop =
1908                                                                 getDiggingProperties(material, toolname);
1909
1910                                                 if(prop.diggable == false)
1911                                                 {
1912                                                         derr_server<<"Server: WARNING: Player digged"
1913                                                                         <<" with impossible material + tool"
1914                                                                         <<" combination"<<std::endl;
1915                                                 }
1916                                                 
1917                                                 bool weared_out = titem->addWear(prop.wear);
1918
1919                                                 if(weared_out)
1920                                                 {
1921                                                         mlist->deleteItem(item_i);
1922                                                 }
1923                                         }
1924                                 }
1925
1926                                 /*
1927                                         Add digged item to inventory
1928                                 */
1929                                 InventoryItem *item = new MaterialItem(material, 1);
1930                                 player->inventory.addItem("main", item);
1931
1932                                 /*
1933                                         Send inventory
1934                                 */
1935                                 SendInventory(player->peer_id);
1936                         }
1937
1938                         /*
1939                                 Remove the node
1940                                 (this takes some time so it is done after the quick stuff)
1941                         */
1942                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1943                         
1944                         /*
1945                                 Update water
1946                         */
1947                         
1948                         // Update water pressure around modification
1949                         // This also adds it to m_flow_active_nodes if appropriate
1950
1951                         MapVoxelManipulator v(&m_env.getMap());
1952                         v.m_disable_water_climb =
1953                                         g_settings.getBool("disable_water_climb");
1954                         
1955                         VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1956
1957                         try
1958                         {
1959                                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1960                         }
1961                         catch(ProcessingLimitException &e)
1962                         {
1963                                 dstream<<"Processing limit reached (1)"<<std::endl;
1964                         }
1965                         
1966                         v.blitBack(modified_blocks);
1967                 }
1968                 
1969                 /*
1970                         1: place block
1971                 */
1972                 else if(action == 1)
1973                 {
1974
1975                         InventoryList *ilist = player->inventory.getList("main");
1976                         if(ilist == NULL)
1977                                 return;
1978
1979                         // Get item
1980                         InventoryItem *item = ilist->getItem(item_i);
1981                         
1982                         // If there is no item, it is not possible to add it anywhere
1983                         if(item == NULL)
1984                                 return;
1985                         
1986                         /*
1987                                 Handle material items
1988                         */
1989                         if(std::string("MaterialItem") == item->getName())
1990                         {
1991                                 try{
1992                                         // Don't add a node if this is not a free space
1993                                         MapNode n2 = m_env.getMap().getNode(p_over);
1994                                         if(content_buildable_to(n2.d) == false)
1995                                                 return;
1996                                 }
1997                                 catch(InvalidPositionException &e)
1998                                 {
1999                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2000                                                         <<std::endl;
2001                                         return;
2002                                 }
2003
2004                                 // Reset build time counter
2005                                 getClient(peer->id)->m_time_from_building.set(0.0);
2006                                 
2007                                 // Create node data
2008                                 MaterialItem *mitem = (MaterialItem*)item;
2009                                 MapNode n;
2010                                 n.d = mitem->getMaterial();
2011                                 if(content_directional(n.d))
2012                                         n.dir = packDir(p_under - p_over);
2013
2014 #if 1
2015                                 // Create packet
2016                                 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2017                                 SharedBuffer<u8> reply(replysize);
2018                                 writeU16(&reply[0], TOCLIENT_ADDNODE);
2019                                 writeS16(&reply[2], p_over.X);
2020                                 writeS16(&reply[4], p_over.Y);
2021                                 writeS16(&reply[6], p_over.Z);
2022                                 n.serialize(&reply[8], peer_ser_ver);
2023                                 // Send as reliable
2024                                 m_con.SendToAll(0, reply, true);
2025                                 
2026                                 /*
2027                                         Handle inventory
2028                                 */
2029                                 InventoryList *ilist = player->inventory.getList("main");
2030                                 if(g_settings.getBool("creative_mode") == false && ilist)
2031                                 {
2032                                         // Remove from inventory and send inventory
2033                                         if(mitem->getCount() == 1)
2034                                                 ilist->deleteItem(item_i);
2035                                         else
2036                                                 mitem->remove(1);
2037                                         // Send inventory
2038                                         SendInventory(peer_id);
2039                                 }
2040                                 
2041                                 /*
2042                                         Add node.
2043
2044                                         This takes some time so it is done after the quick stuff
2045                                 */
2046                                 core::map<v3s16, MapBlock*> modified_blocks;
2047                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2048 #endif
2049 #if 0
2050                                 /*
2051                                         Handle inventory
2052                                 */
2053                                 InventoryList *ilist = player->inventory.getList("main");
2054                                 if(g_settings.getBool("creative_mode") == false && ilist)
2055                                 {
2056                                         // Remove from inventory and send inventory
2057                                         if(mitem->getCount() == 1)
2058                                                 ilist->deleteItem(item_i);
2059                                         else
2060                                                 mitem->remove(1);
2061                                         // Send inventory
2062                                         SendInventory(peer_id);
2063                                 }
2064
2065                                 /*
2066                                         Add node.
2067
2068                                         This takes some time so it is done after the quick stuff
2069                                 */
2070                                 core::map<v3s16, MapBlock*> modified_blocks;
2071                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2072
2073                                 /*
2074                                         Set the modified blocks unsent for all the clients
2075                                 */
2076                                 
2077                                 //JMutexAutoLock lock2(m_con_mutex);
2078
2079                                 for(core::map<u16, RemoteClient*>::Iterator
2080                                                 i = m_clients.getIterator();
2081                                                 i.atEnd() == false; i++)
2082                                 {
2083                                         RemoteClient *client = i.getNode()->getValue();
2084                                         
2085                                         if(modified_blocks.size() > 0)
2086                                         {
2087                                                 // Remove block from sent history
2088                                                 client->SetBlocksNotSent(modified_blocks);
2089                                         }
2090                                 }
2091 #endif
2092                                 
2093                                 /*
2094                                         Update water
2095                                 */
2096                                 
2097                                 // Update water pressure around modification
2098                                 // This also adds it to m_flow_active_nodes if appropriate
2099
2100                                 MapVoxelManipulator v(&m_env.getMap());
2101                                 v.m_disable_water_climb =
2102                                                 g_settings.getBool("disable_water_climb");
2103                                 
2104                                 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2105
2106                                 try
2107                                 {
2108                                         v.updateAreaWaterPressure(area, m_flow_active_nodes);
2109                                 }
2110                                 catch(ProcessingLimitException &e)
2111                                 {
2112                                         dstream<<"Processing limit reached (1)"<<std::endl;
2113                                 }
2114                                 
2115                                 v.blitBack(modified_blocks);
2116                         }
2117                         /*
2118                                 Handle other items
2119                         */
2120                         else
2121                         {
2122                                 v3s16 blockpos = getNodeBlockPos(p_over);
2123
2124                                 MapBlock *block = NULL;
2125                                 try
2126                                 {
2127                                         block = m_env.getMap().getBlockNoCreate(blockpos);
2128                                 }
2129                                 catch(InvalidPositionException &e)
2130                                 {
2131                                         derr_server<<"Error while placing object: "
2132                                                         "block not found"<<std::endl;
2133                                         return;
2134                                 }
2135
2136                                 v3s16 block_pos_i_on_map = block->getPosRelative();
2137                                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2138
2139                                 v3f pos = intToFloat(p_over);
2140                                 pos -= block_pos_f_on_map;
2141                                 
2142                                 /*dout_server<<"pos="
2143                                                 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2144                                                 <<std::endl;*/
2145
2146                                 MapBlockObject *obj = NULL;
2147
2148                                 /*
2149                                         Handle block object items
2150                                 */
2151                                 if(std::string("MBOItem") == item->getName())
2152                                 {
2153                                         MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2154
2155                                         /*dout_server<<"Trying to place a MapBlockObjectItem: "
2156                                                         "inventorystring=\""
2157                                                         <<oitem->getInventoryString()
2158                                                         <<"\""<<std::endl;*/
2159                                                         
2160                                         obj = oitem->createObject
2161                                                         (pos, player->getYaw(), player->getPitch());
2162                                 }
2163                                 /*
2164                                         Handle other items
2165                                 */
2166                                 else
2167                                 {
2168                                         dout_server<<"Placing a miscellaneous item on map"
2169                                                         <<std::endl;
2170                                         /*
2171                                                 Create an ItemObject that contains the item.
2172                                         */
2173                                         ItemObject *iobj = new ItemObject(NULL, -1, pos);
2174                                         std::ostringstream os(std::ios_base::binary);
2175                                         item->serialize(os);
2176                                         dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2177                                         iobj->setItemString(os.str());
2178                                         obj = iobj;
2179                                 }
2180
2181                                 if(obj == NULL)
2182                                 {
2183                                         derr_server<<"WARNING: item resulted in NULL object, "
2184                                                         <<"not placing onto map"
2185                                                         <<std::endl;
2186                                 }
2187                                 else
2188                                 {
2189                                         block->addObject(obj);
2190
2191                                         dout_server<<"Placed object"<<std::endl;
2192
2193                                         InventoryList *ilist = player->inventory.getList("main");
2194                                         if(g_settings.getBool("creative_mode") == false && ilist)
2195                                         {
2196                                                 // Remove from inventory and send inventory
2197                                                 ilist->deleteItem(item_i);
2198                                                 // Send inventory
2199                                                 SendInventory(peer_id);
2200                                         }
2201                                 }
2202                         }
2203
2204                 } // action == 1
2205
2206                 /*
2207                         Catch invalid actions
2208                 */
2209                 else
2210                 {
2211                         derr_server<<"WARNING: Server: Invalid action "
2212                                         <<action<<std::endl;
2213                 }
2214         }
2215 #if 0
2216         else if(command == TOSERVER_RELEASE)
2217         {
2218                 if(datasize < 3)
2219                         return;
2220                 /*
2221                         length: 3
2222                         [0] u16 command
2223                         [2] u8 button
2224                 */
2225                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2226         }
2227 #endif
2228         else if(command == TOSERVER_SIGNTEXT)
2229         {
2230                 /*
2231                         u16 command
2232                         v3s16 blockpos
2233                         s16 id
2234                         u16 textlen
2235                         textdata
2236                 */
2237                 std::string datastring((char*)&data[2], datasize-2);
2238                 std::istringstream is(datastring, std::ios_base::binary);
2239                 u8 buf[6];
2240                 // Read stuff
2241                 is.read((char*)buf, 6);
2242                 v3s16 blockpos = readV3S16(buf);
2243                 is.read((char*)buf, 2);
2244                 s16 id = readS16(buf);
2245                 is.read((char*)buf, 2);
2246                 u16 textlen = readU16(buf);
2247                 std::string text;
2248                 for(u16 i=0; i<textlen; i++)
2249                 {
2250                         is.read((char*)buf, 1);
2251                         text += (char)buf[0];
2252                 }
2253
2254                 MapBlock *block = NULL;
2255                 try
2256                 {
2257                         block = m_env.getMap().getBlockNoCreate(blockpos);
2258                 }
2259                 catch(InvalidPositionException &e)
2260                 {
2261                         derr_server<<"Error while setting sign text: "
2262                                         "block not found"<<std::endl;
2263                         return;
2264                 }
2265
2266                 MapBlockObject *obj = block->getObject(id);
2267                 if(obj == NULL)
2268                 {
2269                         derr_server<<"Error while setting sign text: "
2270                                         "object not found"<<std::endl;
2271                         return;
2272                 }
2273                 
2274                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2275                 {
2276                         derr_server<<"Error while setting sign text: "
2277                                         "object is not a sign"<<std::endl;
2278                         return;
2279                 }
2280
2281                 ((SignObject*)obj)->setText(text);
2282
2283                 obj->getBlock()->setChangedFlag();
2284         }
2285         else if(command == TOSERVER_INVENTORY_ACTION)
2286         {
2287                 /*// Ignore inventory changes if in creative mode
2288                 if(g_settings.getBool("creative_mode") == true)
2289                 {
2290                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2291                                         <<std::endl;
2292                         return;
2293                 }*/
2294                 // Strip command and create a stream
2295                 std::string datastring((char*)&data[2], datasize-2);
2296                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2297                 std::istringstream is(datastring, std::ios_base::binary);
2298                 // Create an action
2299                 InventoryAction *a = InventoryAction::deSerialize(is);
2300                 if(a != NULL)
2301                 {
2302                         /*
2303                                 Handle craftresult specially if not in creative mode
2304                         */
2305                         bool disable_action = false;
2306                         if(a->getType() == IACTION_MOVE
2307                                         && g_settings.getBool("creative_mode") == false)
2308                         {
2309                                 IMoveAction *ma = (IMoveAction*)a;
2310                                 // Don't allow moving anything to craftresult
2311                                 if(ma->to_name == "craftresult")
2312                                 {
2313                                         // Do nothing
2314                                         disable_action = true;
2315                                 }
2316                                 // When something is removed from craftresult
2317                                 if(ma->from_name == "craftresult")
2318                                 {
2319                                         disable_action = true;
2320                                         // Remove stuff from craft
2321                                         InventoryList *clist = player->inventory.getList("craft");
2322                                         if(clist)
2323                                         {
2324                                                 u16 count = ma->count;
2325                                                 if(count == 0)
2326                                                         count = 1;
2327                                                 clist->decrementMaterials(count);
2328                                         }
2329                                         // Do action
2330                                         // Feed action to player inventory
2331                                         a->apply(&player->inventory);
2332                                         // Eat it
2333                                         delete a;
2334                                         // If something appeared in craftresult, throw it
2335                                         // in the main list
2336                                         InventoryList *rlist = player->inventory.getList("craftresult");
2337                                         InventoryList *mlist = player->inventory.getList("main");
2338                                         if(rlist && mlist && rlist->getUsedSlots() == 1)
2339                                         {
2340                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
2341                                                 mlist->addItem(item1);
2342                                         }
2343                                 }
2344                         }
2345                         if(disable_action == false)
2346                         {
2347                                 // Feed action to player inventory
2348                                 a->apply(&player->inventory);
2349                                 // Eat it
2350                                 delete a;
2351                         }
2352                         // Send inventory
2353                         SendInventory(player->peer_id);
2354                 }
2355                 else
2356                 {
2357                         dstream<<"TOSERVER_INVENTORY_ACTION: "
2358                                         <<"InventoryAction::deSerialize() returned NULL"
2359                                         <<std::endl;
2360                 }
2361         }
2362         else if(command == TOSERVER_CHAT_MESSAGE)
2363         {
2364                 /*
2365                         u16 command
2366                         u16 length
2367                         wstring message
2368                 */
2369                 u8 buf[6];
2370                 std::string datastring((char*)&data[2], datasize-2);
2371                 std::istringstream is(datastring, std::ios_base::binary);
2372                 
2373                 // Read stuff
2374                 is.read((char*)buf, 2);
2375                 u16 len = readU16(buf);
2376                 
2377                 std::wstring message;
2378                 for(u16 i=0; i<len; i++)
2379                 {
2380                         is.read((char*)buf, 2);
2381                         message += (wchar_t)readU16(buf);
2382                 }
2383
2384                 // Get player name of this client
2385                 std::wstring name = narrow_to_wide(player->getName());
2386
2387                 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2388                 
2389                 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2390
2391                 /*
2392                         Send the message to all other clients
2393                 */
2394                 for(core::map<u16, RemoteClient*>::Iterator
2395                         i = m_clients.getIterator();
2396                         i.atEnd() == false; i++)
2397                 {
2398                         // Get client and check that it is valid
2399                         RemoteClient *client = i.getNode()->getValue();
2400                         assert(client->peer_id == i.getNode()->getKey());
2401                         if(client->serialization_version == SER_FMT_VER_INVALID)
2402                                 continue;
2403
2404                         // Don't send if it's the same one
2405                         if(peer_id == client->peer_id)
2406                                 continue;
2407
2408                         SendChatMessage(client->peer_id, line);
2409                 }
2410         }
2411         else
2412         {
2413                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2414                                 "unknown command "<<command<<std::endl;
2415         }
2416         
2417         } //try
2418         catch(SendFailedException &e)
2419         {
2420                 derr_server<<"Server::ProcessData(): SendFailedException: "
2421                                 <<"what="<<e.what()
2422                                 <<std::endl;
2423         }
2424 }
2425
2426 /*void Server::Send(u16 peer_id, u16 channelnum,
2427                 SharedBuffer<u8> data, bool reliable)
2428 {
2429         JMutexAutoLock lock(m_con_mutex);
2430         m_con.Send(peer_id, channelnum, data, reliable);
2431 }*/
2432
2433 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2434 {
2435         DSTACK(__FUNCTION_NAME);
2436         /*
2437                 Create a packet with the block in the right format
2438         */
2439         
2440         std::ostringstream os(std::ios_base::binary);
2441         block->serialize(os, ver);
2442         std::string s = os.str();
2443         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2444
2445         u32 replysize = 8 + blockdata.getSize();
2446         SharedBuffer<u8> reply(replysize);
2447         v3s16 p = block->getPos();
2448         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2449         writeS16(&reply[2], p.X);
2450         writeS16(&reply[4], p.Y);
2451         writeS16(&reply[6], p.Z);
2452         memcpy(&reply[8], *blockdata, blockdata.getSize());
2453
2454         /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2455                         <<":  \tpacket size: "<<replysize<<std::endl;*/
2456         
2457         /*
2458                 Send packet
2459         */
2460         m_con.Send(peer_id, 1, reply, true);
2461 }
2462
2463 core::list<PlayerInfo> Server::getPlayerInfo()
2464 {
2465         DSTACK(__FUNCTION_NAME);
2466         JMutexAutoLock envlock(m_env_mutex);
2467         JMutexAutoLock conlock(m_con_mutex);
2468         
2469         core::list<PlayerInfo> list;
2470
2471         core::list<Player*> players = m_env.getPlayers();
2472         
2473         core::list<Player*>::Iterator i;
2474         for(i = players.begin();
2475                         i != players.end(); i++)
2476         {
2477                 PlayerInfo info;
2478
2479                 Player *player = *i;
2480
2481                 try{
2482                         con::Peer *peer = m_con.GetPeer(player->peer_id);
2483                         // Copy info from peer to info struct
2484                         info.id = peer->id;
2485                         info.address = peer->address;
2486                         info.avg_rtt = peer->avg_rtt;
2487                 }
2488                 catch(con::PeerNotFoundException &e)
2489                 {
2490                         // Set dummy peer info
2491                         info.id = 0;
2492                         info.address = Address(0,0,0,0,0);
2493                         info.avg_rtt = 0.0;
2494                 }
2495
2496                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2497                 info.position = player->getPosition();
2498
2499                 list.push_back(info);
2500         }
2501
2502         return list;
2503 }
2504
2505 void Server::peerAdded(con::Peer *peer)
2506 {
2507         DSTACK(__FUNCTION_NAME);
2508         dout_server<<"Server::peerAdded(): peer->id="
2509                         <<peer->id<<std::endl;
2510         
2511         PeerChange c;
2512         c.type = PEER_ADDED;
2513         c.peer_id = peer->id;
2514         c.timeout = false;
2515         m_peer_change_queue.push_back(c);
2516 }
2517
2518 void Server::deletingPeer(con::Peer *peer, bool timeout)
2519 {
2520         DSTACK(__FUNCTION_NAME);
2521         dout_server<<"Server::deletingPeer(): peer->id="
2522                         <<peer->id<<", timeout="<<timeout<<std::endl;
2523         
2524         PeerChange c;
2525         c.type = PEER_REMOVED;
2526         c.peer_id = peer->id;
2527         c.timeout = timeout;
2528         m_peer_change_queue.push_back(c);
2529 }
2530
2531 void Server::SendObjectData(float dtime)
2532 {
2533         DSTACK(__FUNCTION_NAME);
2534
2535         core::map<v3s16, bool> stepped_blocks;
2536         
2537         for(core::map<u16, RemoteClient*>::Iterator
2538                 i = m_clients.getIterator();
2539                 i.atEnd() == false; i++)
2540         {
2541                 u16 peer_id = i.getNode()->getKey();
2542                 RemoteClient *client = i.getNode()->getValue();
2543                 assert(client->peer_id == peer_id);
2544                 
2545                 if(client->serialization_version == SER_FMT_VER_INVALID)
2546                         continue;
2547                 
2548                 client->SendObjectData(this, dtime, stepped_blocks);
2549         }
2550 }
2551
2552 void Server::SendPlayerInfos()
2553 {
2554         DSTACK(__FUNCTION_NAME);
2555
2556         //JMutexAutoLock envlock(m_env_mutex);
2557         
2558         // Get connected players
2559         core::list<Player*> players = m_env.getPlayers(true);
2560         
2561         u32 player_count = players.getSize();
2562         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2563
2564         SharedBuffer<u8> data(datasize);
2565         writeU16(&data[0], TOCLIENT_PLAYERINFO);
2566         
2567         u32 start = 2;
2568         core::list<Player*>::Iterator i;
2569         for(i = players.begin();
2570                         i != players.end(); i++)
2571         {
2572                 Player *player = *i;
2573
2574                 /*dstream<<"Server sending player info for player with "
2575                                 "peer_id="<<player->peer_id<<std::endl;*/
2576                 
2577                 writeU16(&data[start], player->peer_id);
2578                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2579                 start += 2+PLAYERNAME_SIZE;
2580         }
2581
2582         //JMutexAutoLock conlock(m_con_mutex);
2583
2584         // Send as reliable
2585         m_con.SendToAll(0, data, true);
2586 }
2587
2588 enum ItemSpecType
2589 {
2590         ITEM_NONE,
2591         ITEM_MATERIAL,
2592         ITEM_CRAFT,
2593         ITEM_TOOL,
2594         ITEM_MBO
2595 };
2596
2597 struct ItemSpec
2598 {
2599         ItemSpec():
2600                 type(ITEM_NONE)
2601         {
2602         }
2603         ItemSpec(enum ItemSpecType a_type, std::string a_name):
2604                 type(a_type),
2605                 name(a_name),
2606                 num(65535)
2607         {
2608         }
2609         ItemSpec(enum ItemSpecType a_type, u16 a_num):
2610                 type(a_type),
2611                 name(""),
2612                 num(a_num)
2613         {
2614         }
2615         enum ItemSpecType type;
2616         // Only other one of these is used
2617         std::string name;
2618         u16 num;
2619 };
2620
2621 /*
2622         items: a pointer to an array of 9 pointers to items
2623         specs: a pointer to an array of 9 ItemSpecs
2624 */
2625 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2626 {
2627         u16 items_min_x = 100;
2628         u16 items_max_x = 100;
2629         u16 items_min_y = 100;
2630         u16 items_max_y = 100;
2631         for(u16 y=0; y<3; y++)
2632         for(u16 x=0; x<3; x++)
2633         {
2634                 if(items[y*3 + x] == NULL)
2635                         continue;
2636                 if(items_min_x == 100 || x < items_min_x)
2637                         items_min_x = x;
2638                 if(items_min_y == 100 || y < items_min_y)
2639                         items_min_y = y;
2640                 if(items_max_x == 100 || x > items_max_x)
2641                         items_max_x = x;
2642                 if(items_max_y == 100 || y > items_max_y)
2643                         items_max_y = y;
2644         }
2645         // No items at all, just return false
2646         if(items_min_x == 100)
2647                 return false;
2648         
2649         u16 items_w = items_max_x - items_min_x + 1;
2650         u16 items_h = items_max_y - items_min_y + 1;
2651
2652         u16 specs_min_x = 100;
2653         u16 specs_max_x = 100;
2654         u16 specs_min_y = 100;
2655         u16 specs_max_y = 100;
2656         for(u16 y=0; y<3; y++)
2657         for(u16 x=0; x<3; x++)
2658         {
2659                 if(specs[y*3 + x].type == ITEM_NONE)
2660                         continue;
2661                 if(specs_min_x == 100 || x < specs_min_x)
2662                         specs_min_x = x;
2663                 if(specs_min_y == 100 || y < specs_min_y)
2664                         specs_min_y = y;
2665                 if(specs_max_x == 100 || x > specs_max_x)
2666                         specs_max_x = x;
2667                 if(specs_max_y == 100 || y > specs_max_y)
2668                         specs_max_y = y;
2669         }
2670         // No specs at all, just return false
2671         if(specs_min_x == 100)
2672                 return false;
2673
2674         u16 specs_w = specs_max_x - specs_min_x + 1;
2675         u16 specs_h = specs_max_y - specs_min_y + 1;
2676
2677         // Different sizes
2678         if(items_w != specs_w || items_h != specs_h)
2679                 return false;
2680
2681         for(u16 y=0; y<specs_h; y++)
2682         for(u16 x=0; x<specs_w; x++)
2683         {
2684                 u16 items_x = items_min_x + x;
2685                 u16 items_y = items_min_y + y;
2686                 u16 specs_x = specs_min_x + x;
2687                 u16 specs_y = specs_min_y + y;
2688                 InventoryItem *item = items[items_y * 3 + items_x];
2689                 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2690                 
2691                 if(spec.type == ITEM_NONE)
2692                 {
2693                         // Has to be no item
2694                         if(item != NULL)
2695                                 return false;
2696                         continue;
2697                 }
2698                 
2699                 // There should be an item
2700                 if(item == NULL)
2701                         return false;
2702
2703                 std::string itemname = item->getName();
2704
2705                 if(spec.type == ITEM_MATERIAL)
2706                 {
2707                         if(itemname != "MaterialItem")
2708                                 return false;
2709                         MaterialItem *mitem = (MaterialItem*)item;
2710                         if(mitem->getMaterial() != spec.num)
2711                                 return false;
2712                 }
2713                 else if(spec.type == ITEM_CRAFT)
2714                 {
2715                         if(itemname != "CraftItem")
2716                                 return false;
2717                         CraftItem *mitem = (CraftItem*)item;
2718                         if(mitem->getSubName() != spec.name)
2719                                 return false;
2720                 }
2721                 else if(spec.type == ITEM_TOOL)
2722                 {
2723                         // Not supported yet
2724                         assert(0);
2725                 }
2726                 else if(spec.type == ITEM_MBO)
2727                 {
2728                         // Not supported yet
2729                         assert(0);
2730                 }
2731                 else
2732                 {
2733                         // Not supported yet
2734                         assert(0);
2735                 }
2736         }
2737
2738         return true;
2739 }
2740
2741 void Server::SendInventory(u16 peer_id)
2742 {
2743         DSTACK(__FUNCTION_NAME);
2744         
2745         Player* player = m_env.getPlayer(peer_id);
2746
2747         /*
2748                 Calculate crafting stuff
2749         */
2750         if(g_settings.getBool("creative_mode") == false)
2751         {
2752                 InventoryList *clist = player->inventory.getList("craft");
2753                 InventoryList *rlist = player->inventory.getList("craftresult");
2754                 if(rlist)
2755                 {
2756                         rlist->clearItems();
2757                 }
2758                 if(clist && rlist)
2759                 {
2760                         InventoryItem *items[9];
2761                         for(u16 i=0; i<9; i++)
2762                         {
2763                                 items[i] = clist->getItem(i);
2764                         }
2765                         
2766                         bool found = false;
2767
2768                         // Wood
2769                         if(!found)
2770                         {
2771                                 ItemSpec specs[9];
2772                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2773                                 if(checkItemCombination(items, specs))
2774                                 {
2775                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2776                                         found = true;
2777                                 }
2778                         }
2779
2780                         // Stick
2781                         if(!found)
2782                         {
2783                                 ItemSpec specs[9];
2784                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2785                                 if(checkItemCombination(items, specs))
2786                                 {
2787                                         rlist->addItem(new CraftItem("Stick", 4));
2788                                         found = true;
2789                                 }
2790                         }
2791
2792                         // Sign
2793                         if(!found)
2794                         {
2795                                 ItemSpec specs[9];
2796                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2797                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2798                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2799                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2800                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2801                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2802                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2803                                 if(checkItemCombination(items, specs))
2804                                 {
2805                                         rlist->addItem(new MapBlockObjectItem("Sign"));
2806                                         found = true;
2807                                 }
2808                         }
2809
2810                         // Torch
2811                         if(!found)
2812                         {
2813                                 ItemSpec specs[9];
2814                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2815                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2816                                 if(checkItemCombination(items, specs))
2817                                 {
2818                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2819                                         found = true;
2820                                 }
2821                         }
2822
2823                         // Wooden pick
2824                         if(!found)
2825                         {
2826                                 ItemSpec specs[9];
2827                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2828                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2829                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2830                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2831                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2832                                 if(checkItemCombination(items, specs))
2833                                 {
2834                                         rlist->addItem(new ToolItem("WPick", 0));
2835                                         found = true;
2836                                 }
2837                         }
2838
2839                         // Stone pick
2840                         if(!found)
2841                         {
2842                                 ItemSpec specs[9];
2843                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2844                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2845                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2846                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2847                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2848                                 if(checkItemCombination(items, specs))
2849                                 {
2850                                         rlist->addItem(new ToolItem("STPick", 0));
2851                                         found = true;
2852                                 }
2853                         }
2854
2855                         // Mese pick
2856                         if(!found)
2857                         {
2858                                 ItemSpec specs[9];
2859                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2860                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2861                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2862                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2863                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2864                                 if(checkItemCombination(items, specs))
2865                                 {
2866                                         rlist->addItem(new ToolItem("MesePick", 0));
2867                                         found = true;
2868                                 }
2869                         }
2870                 }
2871         } // if creative_mode == false
2872
2873         /*
2874                 Serialize it
2875         */
2876
2877         std::ostringstream os;
2878         //os.imbue(std::locale("C"));
2879
2880         player->inventory.serialize(os);
2881
2882         std::string s = os.str();
2883         
2884         SharedBuffer<u8> data(s.size()+2);
2885         writeU16(&data[0], TOCLIENT_INVENTORY);
2886         memcpy(&data[2], s.c_str(), s.size());
2887         
2888         // Send as reliable
2889         m_con.Send(peer_id, 0, data, true);
2890 }
2891
2892 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2893 {
2894         DSTACK(__FUNCTION_NAME);
2895         
2896         std::ostringstream os(std::ios_base::binary);
2897         u8 buf[12];
2898         
2899         // Write command
2900         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2901         os.write((char*)buf, 2);
2902         
2903         // Write length
2904         writeU16(buf, message.size());
2905         os.write((char*)buf, 2);
2906         
2907         // Write string
2908         for(u32 i=0; i<message.size(); i++)
2909         {
2910                 u16 w = message[i];
2911                 writeU16(buf, w);
2912                 os.write((char*)buf, 2);
2913         }
2914         
2915         // Make data buffer
2916         std::string s = os.str();
2917         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2918         // Send as reliable
2919         m_con.Send(peer_id, 0, data, true);
2920 }
2921
2922 void Server::BroadcastChatMessage(const std::wstring &message)
2923 {
2924         for(core::map<u16, RemoteClient*>::Iterator
2925                 i = m_clients.getIterator();
2926                 i.atEnd() == false; i++)
2927         {
2928                 // Get client and check that it is valid
2929                 RemoteClient *client = i.getNode()->getValue();
2930                 assert(client->peer_id == i.getNode()->getKey());
2931                 if(client->serialization_version == SER_FMT_VER_INVALID)
2932                         continue;
2933
2934                 SendChatMessage(client->peer_id, message);
2935         }
2936 }
2937
2938 void Server::SendBlocks(float dtime)
2939 {
2940         DSTACK(__FUNCTION_NAME);
2941
2942         JMutexAutoLock envlock(m_env_mutex);
2943
2944         core::array<PrioritySortedBlockTransfer> queue;
2945
2946         s32 total_sending = 0;
2947
2948         for(core::map<u16, RemoteClient*>::Iterator
2949                 i = m_clients.getIterator();
2950                 i.atEnd() == false; i++)
2951         {
2952                 RemoteClient *client = i.getNode()->getValue();
2953                 assert(client->peer_id == i.getNode()->getKey());
2954
2955                 total_sending += client->SendingCount();
2956                 
2957                 if(client->serialization_version == SER_FMT_VER_INVALID)
2958                         continue;
2959                 
2960                 client->GetNextBlocks(this, dtime, queue);
2961         }
2962
2963         // Sort.
2964         // Lowest priority number comes first.
2965         // Lowest is most important.
2966         queue.sort();
2967
2968         JMutexAutoLock conlock(m_con_mutex);
2969
2970         for(u32 i=0; i<queue.size(); i++)
2971         {
2972                 //TODO: Calculate limit dynamically
2973                 if(total_sending >= g_settings.getS32
2974                                 ("max_simultaneous_block_sends_server_total"))
2975                         break;
2976                 
2977                 PrioritySortedBlockTransfer q = queue[i];
2978
2979                 MapBlock *block = NULL;
2980                 try
2981                 {
2982                         block = m_env.getMap().getBlockNoCreate(q.pos);
2983                 }
2984                 catch(InvalidPositionException &e)
2985                 {
2986                         continue;
2987                 }
2988
2989                 RemoteClient *client = getClient(q.peer_id);
2990
2991                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
2992
2993                 client->SentBlock(q.pos);
2994
2995                 total_sending++;
2996         }
2997 }
2998
2999
3000 RemoteClient* Server::getClient(u16 peer_id)
3001 {
3002         DSTACK(__FUNCTION_NAME);
3003         //JMutexAutoLock lock(m_con_mutex);
3004         core::map<u16, RemoteClient*>::Node *n;
3005         n = m_clients.find(peer_id);
3006         // A client should exist for all peers
3007         assert(n != NULL);
3008         return n->getValue();
3009 }
3010
3011 Player *Server::emergePlayer(const char *name, const char *password)
3012 {
3013         /*
3014                 Try to get an existing player
3015         */
3016         Player *player = m_env.getPlayer(name);
3017         if(player != NULL)
3018         {
3019                 // Got one.
3020                 return player;
3021         }
3022         
3023         /*
3024                 Create a new player
3025         */
3026         {
3027                 player = new ServerRemotePlayer();
3028                 //player->peer_id = c.peer_id;
3029                 player->peer_id = PEER_ID_INEXISTENT;
3030                 player->updateName(name);
3031
3032                 /*
3033                         Set player position
3034                 */
3035                 
3036                 dstream<<"Server: Finding spawn place for player \""
3037                                 <<player->getName()<<"\""<<std::endl;
3038
3039 #if 1
3040                 v2s16 nodepos;
3041                 f32 groundheight = 0;
3042                 // Try to find a good place a few times
3043                 for(s32 i=0; i<100; i++)
3044                 {
3045                         s32 range = 1 + i*4;
3046                         // We're going to try to throw the player to this position
3047                         nodepos = v2s16(-range/2 + (myrand()%range),
3048                                         -range/2 + (myrand()%range));
3049                         v2s16 sectorpos = getNodeSectorPos(nodepos);
3050                         // Get sector
3051                         m_env.getMap().emergeSector(sectorpos);
3052                         // Get ground height at point
3053                         groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3054                         // The sector should have been generated -> groundheight exists
3055                         assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3056                         // Don't go underwater
3057                         if(groundheight < WATER_LEVEL)
3058                                 continue;
3059                         // Don't go inside ground
3060                         try{
3061                                 v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3062                                 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);
3063                                 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3064                                         || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3065                                 {
3066                                         // In ground
3067                                         continue;
3068                                 }
3069                         }catch(InvalidPositionException &e)
3070                         {
3071                                 // Ignore invalid position
3072                                 continue;
3073                         }
3074                         // Found a good place
3075                         break;
3076                 }
3077 #endif
3078                 
3079                 // If no suitable place was not found, go above water at least.
3080                 if(groundheight < WATER_LEVEL)
3081                         groundheight = WATER_LEVEL;
3082
3083                 player->setPosition(intToFloat(v3s16(
3084                                 nodepos.X,
3085                                 groundheight + 1,
3086                                 nodepos.Y
3087                 )));
3088
3089                 /*
3090                         Add player to environment
3091                 */
3092
3093                 m_env.addPlayer(player);
3094
3095                 /*
3096                         Add stuff to inventory
3097                 */
3098                 
3099                 if(g_settings.getBool("creative_mode"))
3100                 {
3101                         // Give some good picks
3102                         {
3103                                 InventoryItem *item = new ToolItem("STPick", 0);
3104                                 void* r = player->inventory.addItem("main", item);
3105                                 assert(r == NULL);
3106                         }
3107                         {
3108                                 InventoryItem *item = new ToolItem("MesePick", 0);
3109                                 void* r = player->inventory.addItem("main", item);
3110                                 assert(r == NULL);
3111                         }
3112
3113                         /*
3114                                 Give materials
3115                         */
3116                         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3117                         
3118                         // add torch first
3119                         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3120                         player->inventory.addItem("main", item);
3121                         
3122                         // Then others
3123                         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3124                         {
3125                                 // Skip some materials
3126                                 if(i == CONTENT_OCEAN || i == CONTENT_TORCH)
3127                                         continue;
3128
3129                                 InventoryItem *item = new MaterialItem(i, 1);
3130                                 player->inventory.addItem("main", item);
3131                         }
3132                         // Sign
3133                         {
3134                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3135                                 void* r = player->inventory.addItem("main", item);
3136                                 assert(r == NULL);
3137                         }
3138                 }
3139                 else
3140                 {
3141                         /*{
3142                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3143                                 void* r = player->inventory.addItem("main", item);
3144                                 assert(r == NULL);
3145                         }
3146                         {
3147                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3148                                 void* r = player->inventory.addItem("main", item);
3149                                 assert(r == NULL);
3150                         }
3151                         {
3152                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3153                                 void* r = player->inventory.addItem("main", item);
3154                                 assert(r == NULL);
3155                         }
3156                         {
3157                                 InventoryItem *item = new CraftItem("Stick", 4);
3158                                 void* r = player->inventory.addItem("main", item);
3159                                 assert(r == NULL);
3160                         }
3161                         {
3162                                 InventoryItem *item = new ToolItem("WPick", 32000);
3163                                 void* r = player->inventory.addItem("main", item);
3164                                 assert(r == NULL);
3165                         }
3166                         {
3167                                 InventoryItem *item = new ToolItem("STPick", 32000);
3168                                 void* r = player->inventory.addItem("main", item);
3169                                 assert(r == NULL);
3170                         }*/
3171                         /*// Give some lights
3172                         {
3173                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3174                                 bool r = player->inventory.addItem("main", item);
3175                                 assert(r == true);
3176                         }
3177                         // and some signs
3178                         for(u16 i=0; i<4; i++)
3179                         {
3180                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3181                                 bool r = player->inventory.addItem("main", item);
3182                                 assert(r == true);
3183                         }*/
3184                         /*// Give some other stuff
3185                         {
3186                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3187                                 bool r = player->inventory.addItem("main", item);
3188                                 assert(r == true);
3189                         }*/
3190                 }
3191
3192                 return player;
3193         }
3194 }
3195
3196 void Server::UpdateBlockWaterPressure(MapBlock *block,
3197                         core::map<v3s16, MapBlock*> &modified_blocks)
3198 {
3199         MapVoxelManipulator v(&m_env.getMap());
3200         v.m_disable_water_climb =
3201                         g_settings.getBool("disable_water_climb");
3202         
3203         VoxelArea area(block->getPosRelative(),
3204                         block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3205
3206         try
3207         {
3208                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3209         }
3210         catch(ProcessingLimitException &e)
3211         {
3212                 dstream<<"Processing limit reached (1)"<<std::endl;
3213         }
3214         
3215         v.blitBack(modified_blocks);
3216 }
3217
3218 void Server::handlePeerChange(PeerChange &c)
3219 {
3220         JMutexAutoLock envlock(m_env_mutex);
3221         JMutexAutoLock conlock(m_con_mutex);
3222         
3223         if(c.type == PEER_ADDED)
3224         {
3225                 /*
3226                         Add
3227                 */
3228
3229                 // Error check
3230                 core::map<u16, RemoteClient*>::Node *n;
3231                 n = m_clients.find(c.peer_id);
3232                 // The client shouldn't already exist
3233                 assert(n == NULL);
3234
3235                 // Create client
3236                 RemoteClient *client = new RemoteClient();
3237                 client->peer_id = c.peer_id;
3238                 m_clients.insert(client->peer_id, client);
3239
3240         } // PEER_ADDED
3241         else if(c.type == PEER_REMOVED)
3242         {
3243                 /*
3244                         Delete
3245                 */
3246
3247                 // Error check
3248                 core::map<u16, RemoteClient*>::Node *n;
3249                 n = m_clients.find(c.peer_id);
3250                 // The client should exist
3251                 assert(n != NULL);
3252                 
3253                 // Collect information about leaving in chat
3254                 std::wstring message;
3255                 {
3256                         std::wstring name = L"unknown";
3257                         Player *player = m_env.getPlayer(c.peer_id);
3258                         if(player != NULL)
3259                                 name = narrow_to_wide(player->getName());
3260                         
3261                         message += L"*** ";
3262                         message += name;
3263                         message += L" left game";
3264                         if(c.timeout)
3265                                 message += L" (timed out)";
3266                 }
3267
3268                 /*// Delete player
3269                 {
3270                         m_env.removePlayer(c.peer_id);
3271                 }*/
3272
3273                 // Set player client disconnected
3274                 {
3275                         Player *player = m_env.getPlayer(c.peer_id);
3276                         player->peer_id = 0;
3277                 }
3278                 
3279                 // Delete client
3280                 delete m_clients[c.peer_id];
3281                 m_clients.remove(c.peer_id);
3282
3283                 // Send player info to all remaining clients
3284                 SendPlayerInfos();
3285                 
3286                 // Send leave chat message to all remaining clients
3287                 BroadcastChatMessage(message);
3288                 
3289         } // PEER_REMOVED
3290         else
3291         {
3292                 assert(0);
3293         }
3294 }
3295
3296 void Server::handlePeerChanges()
3297 {
3298         while(m_peer_change_queue.size() > 0)
3299         {
3300                 PeerChange c = m_peer_change_queue.pop_front();
3301
3302                 dout_server<<"Server: Handling peer change: "
3303                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3304                                 <<std::endl;
3305
3306                 handlePeerChange(c);
3307         }
3308 }
3309
3310