d47dcb9ce1ad9cd46e17cbfda3b187c40bedd2f1
[oweals/minetest.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 #include "server.h"
21 #include "utility.h"
22 #include <iostream>
23 #include "clientserver.h"
24 #include "map.h"
25 #include "jmutexautolock.h"
26 #include "main.h"
27 #include "constants.h"
28 #include "voxel.h"
29 #include "materials.h"
30 #include "mineral.h"
31 #include "config.h"
32
33 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
34
35 void * ServerThread::Thread()
36 {
37         ThreadStarted();
38
39         DSTACK(__FUNCTION_NAME);
40
41         BEGIN_DEBUG_EXCEPTION_HANDLER
42
43         while(getRun())
44         {
45                 try{
46                         //TimeTaker timer("AsyncRunStep() + Receive()");
47
48                         {
49                                 //TimeTaker timer("AsyncRunStep()");
50                                 m_server->AsyncRunStep();
51                         }
52                 
53                         //dout_server<<"Running m_server->Receive()"<<std::endl;
54                         m_server->Receive();
55                 }
56                 catch(con::NoIncomingDataException &e)
57                 {
58                 }
59                 catch(con::PeerNotFoundException &e)
60                 {
61                         dout_server<<"Server: PeerNotFoundException"<<std::endl;
62                 }
63         }
64         
65         END_DEBUG_EXCEPTION_HANDLER
66
67         return NULL;
68 }
69
70 void * EmergeThread::Thread()
71 {
72         ThreadStarted();
73
74         DSTACK(__FUNCTION_NAME);
75
76         //bool debug=false;
77         
78         BEGIN_DEBUG_EXCEPTION_HANDLER
79
80         /*
81                 Get block info from queue, emerge them and send them
82                 to clients.
83
84                 After queue is empty, exit.
85         */
86         while(getRun())
87         {
88                 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
89                 if(qptr == NULL)
90                         break;
91                 
92                 SharedPtr<QueuedBlockEmerge> q(qptr);
93
94                 v3s16 &p = q->pos;
95                 v2s16 p2d(p.X,p.Z);
96
97                 /*
98                         Do not generate over-limit
99                 */
100                 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
101                 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
102                 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103                 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104                 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105                 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
106                         continue;
107                         
108                 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
109
110                 //TimeTaker timer("block emerge");
111                 
112                 /*
113                         Try to emerge it from somewhere.
114
115                         If it is only wanted as optional, only loading from disk
116                         will be allowed.
117                 */
118                 
119                 /*
120                         Check if any peer wants it as non-optional. In that case it
121                         will be generated.
122
123                         Also decrement the emerge queue count in clients.
124                 */
125
126                 bool optional = true;
127
128                 {
129                         core::map<u16, u8>::Iterator i;
130                         for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
131                         {
132                                 //u16 peer_id = i.getNode()->getKey();
133
134                                 // Check flags
135                                 u8 flags = i.getNode()->getValue();
136                                 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
137                                         optional = false;
138                                 
139                         }
140                 }
141
142                 /*dstream<<"EmergeThread: p="
143                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
144                                 <<"optional="<<optional<<std::endl;*/
145                 
146                 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
147                         
148                 core::map<v3s16, MapBlock*> changed_blocks;
149                 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
150
151                 MapBlock *block = NULL;
152                 bool got_block = true;
153                 core::map<v3s16, MapBlock*> modified_blocks;
154                 
155                 bool only_from_disk = false;
156                 
157                 if(optional)
158                         only_from_disk = true;
159
160                 v2s16 chunkpos = map.sector_to_chunk(p2d);
161
162                 bool generate_chunk = false;
163                 if(only_from_disk == false)
164                 {
165                         JMutexAutoLock envlock(m_server->m_env_mutex);
166                         if(map.chunkNonVolatile(chunkpos) == false)
167                                 generate_chunk = true;
168                 }
169                 if(generate_chunk)
170                 {
171                         ChunkMakeData data;
172                         
173                         {
174                                 JMutexAutoLock envlock(m_server->m_env_mutex);
175                                 map.initChunkMake(data, chunkpos);
176                         }
177
178                         makeChunk(&data);
179
180                         {
181                                 JMutexAutoLock envlock(m_server->m_env_mutex);
182                                 map.finishChunkMake(data, changed_blocks);
183                         }
184                 }
185         
186                 /*
187                         Fetch block from map or generate a single block
188                 */
189                 {
190                         JMutexAutoLock envlock(m_server->m_env_mutex);
191                         
192                         // Load sector if it isn't loaded
193                         if(map.getSectorNoGenerateNoEx(p2d) == NULL)
194                                 map.loadSectorFull(p2d);
195
196                         block = map.getBlockNoCreateNoEx(p);
197                         if(!block || block->isDummy())
198                         {
199                                 if(only_from_disk)
200                                 {
201                                         got_block = false;
202                                 }
203                                 else
204                                 {
205                                         // Get, load or create sector
206                                         ServerMapSector *sector =
207                                                         (ServerMapSector*)map.createSector(p2d);
208                                         // Generate block
209                                         block = map.generateBlock(p, block, sector, changed_blocks,
210                                                         lighting_invalidated_blocks);
211                                         if(block == NULL)
212                                                 got_block = false;
213                                 }
214                         }
215                         else
216                         {
217                                 if(block->getLightingExpired()){
218                                         lighting_invalidated_blocks[block->getPos()] = block;
219                                 }
220                         }
221
222                         // TODO: Some additional checking and lighting updating,
223                         // see emergeBlock
224                 }
225
226                 {//envlock
227                 JMutexAutoLock envlock(m_server->m_env_mutex);
228                 
229                 if(got_block)
230                 {
231                         /*
232                                 Collect a list of blocks that have been modified in
233                                 addition to the fetched one.
234                         */
235                         
236                         if(lighting_invalidated_blocks.size() > 0)
237                         {
238                                 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
239                                                 <<" blocks"<<std::endl;*/
240                         
241                                 // 50-100ms for single block generation
242                                 //TimeTaker timer("** EmergeThread updateLighting");
243                                 
244                                 // Update lighting without locking the environment mutex,
245                                 // add modified blocks to changed blocks
246                                 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
247                         }
248                                 
249                         // Add all from changed_blocks to modified_blocks
250                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
251                                         i.atEnd() == false; i++)
252                         {
253                                 MapBlock *block = i.getNode()->getValue();
254                                 modified_blocks.insert(block->getPos(), block);
255                         }
256                 }
257                 // If we got no block, there should be no invalidated blocks
258                 else
259                 {
260                         assert(lighting_invalidated_blocks.size() == 0);
261                 }
262
263                 }//envlock
264
265                 /*
266                         Set sent status of modified blocks on clients
267                 */
268         
269                 // NOTE: Server's clients are also behind the connection mutex
270                 JMutexAutoLock lock(m_server->m_con_mutex);
271
272                 /*
273                         Add the originally fetched block to the modified list
274                 */
275                 if(got_block)
276                 {
277                         modified_blocks.insert(p, block);
278                 }
279                 
280                 /*
281                         Set the modified blocks unsent for all the clients
282                 */
283                 
284                 for(core::map<u16, RemoteClient*>::Iterator
285                                 i = m_server->m_clients.getIterator();
286                                 i.atEnd() == false; i++)
287                 {
288                         RemoteClient *client = i.getNode()->getValue();
289                         
290                         if(modified_blocks.size() > 0)
291                         {
292                                 // Remove block from sent history
293                                 client->SetBlocksNotSent(modified_blocks);
294                         }
295                 }
296                 
297         }
298
299         END_DEBUG_EXCEPTION_HANDLER
300
301         return NULL;
302 }
303
304 void RemoteClient::GetNextBlocks(Server *server, float dtime,
305                 core::array<PrioritySortedBlockTransfer> &dest)
306 {
307         DSTACK(__FUNCTION_NAME);
308         
309         /*u32 timer_result;
310         TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
311         
312         // Increment timers
313         m_nearest_unsent_reset_timer += dtime;
314         m_nothing_to_send_pause_timer -= dtime;
315         
316         if(m_nothing_to_send_pause_timer >= 0)
317                 return;
318
319         // Won't send anything if already sending
320         if(m_blocks_sending.size() >= g_settings.getU16
321                         ("max_simultaneous_block_sends_per_client"))
322         {
323                 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
324                 return;
325         }
326
327         Player *player = server->m_env.getPlayer(peer_id);
328
329         assert(player != NULL);
330
331         v3f playerpos = player->getPosition();
332         v3f playerspeed = player->getSpeed();
333
334         v3s16 center_nodepos = floatToInt(playerpos, BS);
335
336         v3s16 center = getNodeBlockPos(center_nodepos);
337         
338         // Camera position and direction
339         v3f camera_pos =
340                         playerpos + v3f(0, BS+BS/2, 0);
341         v3f camera_dir = v3f(0,0,1);
342         camera_dir.rotateYZBy(player->getPitch());
343         camera_dir.rotateXZBy(player->getYaw());
344
345         /*
346                 Get the starting value of the block finder radius.
347         */
348                 
349         if(m_last_center != center)
350         {
351                 m_nearest_unsent_d = 0;
352                 m_last_center = center;
353         }
354
355         /*dstream<<"m_nearest_unsent_reset_timer="
356                         <<m_nearest_unsent_reset_timer<<std::endl;*/
357         if(m_nearest_unsent_reset_timer > 5.0)
358         {
359                 m_nearest_unsent_reset_timer = 0;
360                 m_nearest_unsent_d = 0;
361                 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
362         }
363
364         //s16 last_nearest_unsent_d = m_nearest_unsent_d;
365         s16 d_start = m_nearest_unsent_d;
366
367         //dstream<<"d_start="<<d_start<<std::endl;
368
369         u16 max_simul_sends_setting = g_settings.getU16
370                         ("max_simultaneous_block_sends_per_client");
371         u16 max_simul_sends_usually = max_simul_sends_setting;
372
373         /*
374                 Check the time from last addNode/removeNode.
375                 
376                 Decrease send rate if player is building stuff.
377         */
378         m_time_from_building += dtime;
379         if(m_time_from_building < g_settings.getFloat(
380                                 "full_block_send_enable_min_time_from_building"))
381         {
382                 max_simul_sends_usually
383                         = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
384         }
385         
386         /*
387                 Number of blocks sending + number of blocks selected for sending
388         */
389         u32 num_blocks_selected = m_blocks_sending.size();
390         
391         /*
392                 next time d will be continued from the d from which the nearest
393                 unsent block was found this time.
394
395                 This is because not necessarily any of the blocks found this
396                 time are actually sent.
397         */
398         s32 new_nearest_unsent_d = -1;
399
400         s16 d_max = g_settings.getS16("max_block_send_distance");
401         s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
402         
403         //dstream<<"Starting from "<<d_start<<std::endl;
404
405         bool sending_something = false;
406
407         for(s16 d = d_start; d <= d_max; d++)
408         {
409                 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
410                 
411                 /*
412                         If m_nearest_unsent_d was changed by the EmergeThread
413                         (it can change it to 0 through SetBlockNotSent),
414                         update our d to it.
415                         Else update m_nearest_unsent_d
416                 */
417                 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
418                 {
419                         d = m_nearest_unsent_d;
420                         last_nearest_unsent_d = m_nearest_unsent_d;
421                 }*/
422
423                 /*
424                         Get the border/face dot coordinates of a "d-radiused"
425                         box
426                 */
427                 core::list<v3s16> list;
428                 getFacePositions(list, d);
429                 
430                 core::list<v3s16>::Iterator li;
431                 for(li=list.begin(); li!=list.end(); li++)
432                 {
433                         v3s16 p = *li + center;
434                         
435                         /*
436                                 Send throttling
437                                 - Don't allow too many simultaneous transfers
438                                 - EXCEPT when the blocks are very close
439
440                                 Also, don't send blocks that are already flying.
441                         */
442                         
443                         // Start with the usual maximum
444                         u16 max_simul_dynamic = max_simul_sends_usually;
445                         
446                         // If block is very close, allow full maximum
447                         if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
448                                 max_simul_dynamic = max_simul_sends_setting;
449
450                         // Don't select too many blocks for sending
451                         if(num_blocks_selected >= max_simul_dynamic)
452                                 goto queue_full;
453                         
454                         // Don't send blocks that are currently being transferred
455                         if(m_blocks_sending.find(p) != NULL)
456                                 continue;
457                 
458                         /*
459                                 Do not go over-limit
460                         */
461                         if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
462                         || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
463                         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
464                         || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
465                         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
466                         || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
467                                 continue;
468                 
469                         // If this is true, inexistent block will be made from scratch
470                         bool generate = d <= d_max_gen;
471                         
472                         {
473                                 /*// Limit the generating area vertically to 2/3
474                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
475                                         generate = false;*/
476
477                                 // Limit the send area vertically to 2/3
478                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
479                                         continue;
480                         }
481
482 #if 1
483                         /*
484                                 If block is far away, don't generate it unless it is
485                                 near ground level.
486                         */
487                         if(d >= 4)
488                         {
489         #if 1
490                                 // Block center y in nodes
491                                 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
492                                 // Don't generate if it's very high or very low
493                                 if(y < -64 || y > 64)
494                                         generate = false;
495         #endif
496         #if 0
497                                 v2s16 p2d_nodes_center(
498                                         MAP_BLOCKSIZE*p.X,
499                                         MAP_BLOCKSIZE*p.Z);
500                                 
501                                 // Get ground height in nodes
502                                 s16 gh = server->m_env.getServerMap().findGroundLevel(
503                                                 p2d_nodes_center);
504
505                                 // If differs a lot, don't generate
506                                 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
507                                         generate = false;
508                                         // Actually, don't even send it
509                                         //continue;
510         #endif
511                         }
512 #endif
513
514                         /*
515                                 Don't generate or send if not in sight
516                         */
517
518                         if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
519                         {
520                                 continue;
521                         }
522                         
523                         /*
524                                 Don't send already sent blocks
525                         */
526                         {
527                                 if(m_blocks_sent.find(p) != NULL)
528                                         continue;
529                         }
530
531                         /*
532                                 Check if map has this block
533                         */
534                         MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
535                         
536                         bool surely_not_found_on_disk = false;
537                         bool block_is_invalid = false;
538                         if(block != NULL)
539                         {
540                                 if(block->isDummy())
541                                 {
542                                         surely_not_found_on_disk = true;
543                                 }
544
545                                 if(block->isValid() == false)
546                                 {
547                                         block_is_invalid = true;
548                                 }
549                                 
550                                 /*if(block->isFullyGenerated() == false)
551                                 {
552                                         block_is_invalid = true;
553                                 }*/
554                                 
555                                 v2s16 p2d(p.X, p.Z);
556                                 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
557                                 v2s16 chunkpos = map->sector_to_chunk(p2d);
558                                 if(map->chunkNonVolatile(chunkpos) == false)
559                                         block_is_invalid = true;
560 #if 1
561                                 /*
562                                         If block is not close, don't send it unless it is near
563                                         ground level.
564
565                                         Block is not near ground level if night-time mesh
566                                         doesn't differ from day-time mesh.
567                                 */
568                                 if(d > 3)
569                                 {
570                                         if(block->dayNightDiffed() == false)
571                                                 continue;
572                                 }
573 #endif
574                         }
575
576                         /*
577                                 If block has been marked to not exist on disk (dummy)
578                                 and generating new ones is not wanted, skip block.
579                         */
580                         if(generate == false && surely_not_found_on_disk == true)
581                         {
582                                 // get next one.
583                                 continue;
584                         }
585
586                         /*
587                                 Record the lowest d from which a a block has been
588                                 found being not sent and possibly to exist
589                         */
590                         if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
591                         {
592                                 if(generate == true)
593                                         new_nearest_unsent_d = d;
594                         }
595                                         
596                         /*
597                                 Add inexistent block to emerge queue.
598                         */
599                         if(block == NULL || surely_not_found_on_disk || block_is_invalid)
600                         {
601                                 //TODO: Get value from somewhere
602                                 // Allow only one block in emerge queue
603                                 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
604                                 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
605                                 {
606                                         //dstream<<"Adding block to emerge queue"<<std::endl;
607                                         
608                                         // Add it to the emerge queue and trigger the thread
609                                         
610                                         u8 flags = 0;
611                                         if(generate == false)
612                                                 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
613                                         
614                                         server->m_emerge_queue.addBlock(peer_id, p, flags);
615                                         server->m_emergethread.trigger();
616                                 }
617                                 
618                                 // get next one.
619                                 continue;
620                         }
621
622                         /*
623                                 Add block to send queue
624                         */
625
626                         PrioritySortedBlockTransfer q((float)d, p, peer_id);
627
628                         dest.push_back(q);
629
630                         num_blocks_selected += 1;
631                         sending_something = true;
632                 }
633         }
634 queue_full:
635
636         if(new_nearest_unsent_d != -1)
637         {
638                 m_nearest_unsent_d = new_nearest_unsent_d;
639         }
640
641         if(sending_something == false)
642         {
643                 m_nothing_to_send_counter++;
644                 if(m_nothing_to_send_counter >= 3)
645                 {
646                         // Pause time in seconds
647                         m_nothing_to_send_pause_timer = 2.0;
648                 }
649         }
650         else
651         {
652                 m_nothing_to_send_counter = 0;
653         }
654
655         /*timer_result = timer.stop(true);
656         if(timer_result != 0)
657                 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
658 }
659
660 void RemoteClient::SendObjectData(
661                 Server *server,
662                 float dtime,
663                 core::map<v3s16, bool> &stepped_blocks
664         )
665 {
666         DSTACK(__FUNCTION_NAME);
667
668         // Can't send anything without knowing version
669         if(serialization_version == SER_FMT_VER_INVALID)
670         {
671                 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
672                                 <<std::endl;
673                 return;
674         }
675
676         /*
677                 Send a TOCLIENT_OBJECTDATA packet.
678                 Sent as unreliable.
679
680                 u16 command
681                 u16 number of player positions
682                 for each player:
683                         v3s32 position*100
684                         v3s32 speed*100
685                         s32 pitch*100
686                         s32 yaw*100
687                 u16 count of blocks
688                 for each block:
689                         block objects
690         */
691
692         std::ostringstream os(std::ios_base::binary);
693         u8 buf[12];
694         
695         // Write command
696         writeU16(buf, TOCLIENT_OBJECTDATA);
697         os.write((char*)buf, 2);
698         
699         /*
700                 Get and write player data
701         */
702         
703         // Get connected players
704         core::list<Player*> players = server->m_env.getPlayers(true);
705
706         // Write player count
707         u16 playercount = players.size();
708         writeU16(buf, playercount);
709         os.write((char*)buf, 2);
710
711         core::list<Player*>::Iterator i;
712         for(i = players.begin();
713                         i != players.end(); i++)
714         {
715                 Player *player = *i;
716
717                 v3f pf = player->getPosition();
718                 v3f sf = player->getSpeed();
719
720                 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
721                 v3s32 speed_i   (sf.X*100, sf.Y*100, sf.Z*100);
722                 s32   pitch_i   (player->getPitch() * 100);
723                 s32   yaw_i     (player->getYaw() * 100);
724                 
725                 writeU16(buf, player->peer_id);
726                 os.write((char*)buf, 2);
727                 writeV3S32(buf, position_i);
728                 os.write((char*)buf, 12);
729                 writeV3S32(buf, speed_i);
730                 os.write((char*)buf, 12);
731                 writeS32(buf, pitch_i);
732                 os.write((char*)buf, 4);
733                 writeS32(buf, yaw_i);
734                 os.write((char*)buf, 4);
735         }
736         
737         /*
738                 Get and write object data
739         */
740
741         /*
742                 Get nearby blocks.
743                 
744                 For making players to be able to build to their nearby
745                 environment (building is not possible on blocks that are not
746                 in memory):
747                 - Set blocks changed
748                 - Add blocks to emerge queue if they are not found
749
750                 SUGGESTION: These could be ignored from the backside of the player
751         */
752
753         Player *player = server->m_env.getPlayer(peer_id);
754
755         assert(player);
756
757         v3f playerpos = player->getPosition();
758         v3f playerspeed = player->getSpeed();
759
760         v3s16 center_nodepos = floatToInt(playerpos, BS);
761         v3s16 center = getNodeBlockPos(center_nodepos);
762
763         s16 d_max = g_settings.getS16("active_object_range");
764         
765         // Number of blocks whose objects were written to bos
766         u16 blockcount = 0;
767
768         std::ostringstream bos(std::ios_base::binary);
769
770         for(s16 d = 0; d <= d_max; d++)
771         {
772                 core::list<v3s16> list;
773                 getFacePositions(list, d);
774                 
775                 core::list<v3s16>::Iterator li;
776                 for(li=list.begin(); li!=list.end(); li++)
777                 {
778                         v3s16 p = *li + center;
779
780                         /*
781                                 Ignore blocks that haven't been sent to the client
782                         */
783                         {
784                                 if(m_blocks_sent.find(p) == NULL)
785                                         continue;
786                         }
787                         
788                         // Try stepping block and add it to a send queue
789                         try
790                         {
791
792                         // Get block
793                         MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
794
795                         /*
796                                 Step block if not in stepped_blocks and add to stepped_blocks.
797                         */
798                         if(stepped_blocks.find(p) == NULL)
799                         {
800                                 block->stepObjects(dtime, true, server->getDayNightRatio());
801                                 stepped_blocks.insert(p, true);
802                                 block->setChangedFlag();
803                         }
804
805                         // Skip block if there are no objects
806                         if(block->getObjectCount() == 0)
807                                 continue;
808                         
809                         /*
810                                 Write objects
811                         */
812
813                         // Write blockpos
814                         writeV3S16(buf, p);
815                         bos.write((char*)buf, 6);
816
817                         // Write objects
818                         block->serializeObjects(bos, serialization_version);
819
820                         blockcount++;
821
822                         /*
823                                 Stop collecting objects if data is already too big
824                         */
825                         // Sum of player and object data sizes
826                         s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
827                         // break out if data too big
828                         if(sum > MAX_OBJECTDATA_SIZE)
829                         {
830                                 goto skip_subsequent;
831                         }
832                         
833                         } //try
834                         catch(InvalidPositionException &e)
835                         {
836                                 // Not in memory
837                                 // Add it to the emerge queue and trigger the thread.
838                                 // Fetch the block only if it is on disk.
839                                 
840                                 // Grab and increment counter
841                                 /*SharedPtr<JMutexAutoLock> lock
842                                                 (m_num_blocks_in_emerge_queue.getLock());
843                                 m_num_blocks_in_emerge_queue.m_value++;*/
844                                 
845                                 // Add to queue as an anonymous fetch from disk
846                                 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
847                                 server->m_emerge_queue.addBlock(0, p, flags);
848                                 server->m_emergethread.trigger();
849                         }
850                 }
851         }
852
853 skip_subsequent:
854
855         // Write block count
856         writeU16(buf, blockcount);
857         os.write((char*)buf, 2);
858
859         // Write block objects
860         os<<bos.str();
861
862         /*
863                 Send data
864         */
865         
866         //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
867
868         // Make data buffer
869         std::string s = os.str();
870         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
871         // Send as unreliable
872         server->m_con.Send(peer_id, 0, data, false);
873 }
874
875 void RemoteClient::GotBlock(v3s16 p)
876 {
877         if(m_blocks_sending.find(p) != NULL)
878                 m_blocks_sending.remove(p);
879         else
880         {
881                 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
882                                 " m_blocks_sending"<<std::endl;*/
883                 m_excess_gotblocks++;
884         }
885         m_blocks_sent.insert(p, true);
886 }
887
888 void RemoteClient::SentBlock(v3s16 p)
889 {
890         if(m_blocks_sending.find(p) == NULL)
891                 m_blocks_sending.insert(p, 0.0);
892         else
893                 dstream<<"RemoteClient::SentBlock(): Sent block"
894                                 " already in m_blocks_sending"<<std::endl;
895 }
896
897 void RemoteClient::SetBlockNotSent(v3s16 p)
898 {
899         m_nearest_unsent_d = 0;
900         
901         if(m_blocks_sending.find(p) != NULL)
902                 m_blocks_sending.remove(p);
903         if(m_blocks_sent.find(p) != NULL)
904                 m_blocks_sent.remove(p);
905 }
906
907 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
908 {
909         m_nearest_unsent_d = 0;
910         
911         for(core::map<v3s16, MapBlock*>::Iterator
912                         i = blocks.getIterator();
913                         i.atEnd()==false; i++)
914         {
915                 v3s16 p = i.getNode()->getKey();
916
917                 if(m_blocks_sending.find(p) != NULL)
918                         m_blocks_sending.remove(p);
919                 if(m_blocks_sent.find(p) != NULL)
920                         m_blocks_sent.remove(p);
921         }
922 }
923
924 /*
925         PlayerInfo
926 */
927
928 PlayerInfo::PlayerInfo()
929 {
930         name[0] = 0;
931         avg_rtt = 0;
932 }
933
934 void PlayerInfo::PrintLine(std::ostream *s)
935 {
936         (*s)<<id<<": ";
937         (*s)<<"\""<<name<<"\" ("
938                         <<(position.X/10)<<","<<(position.Y/10)
939                         <<","<<(position.Z/10)<<") ";
940         address.print(s);
941         (*s)<<" avg_rtt="<<avg_rtt;
942         (*s)<<std::endl;
943 }
944
945 u32 PIChecksum(core::list<PlayerInfo> &l)
946 {
947         core::list<PlayerInfo>::Iterator i;
948         u32 checksum = 1;
949         u32 a = 10;
950         for(i=l.begin(); i!=l.end(); i++)
951         {
952                 checksum += a * (i->id+1);
953                 checksum ^= 0x435aafcd;
954                 a *= 10;
955         }
956         return checksum;
957 }
958
959 /*
960         Server
961 */
962
963 Server::Server(
964                 std::string mapsavedir
965         ):
966         m_env(new ServerMap(mapsavedir), this),
967         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
968         m_thread(this),
969         m_emergethread(this),
970         m_time_of_day(9000),
971         m_time_counter(0),
972         m_time_of_day_send_timer(0),
973         m_uptime(0),
974         m_mapsavedir(mapsavedir),
975         m_shutdown_requested(false),
976         m_ignore_map_edit_events(false),
977         m_ignore_map_edit_events_peer_id(0)
978 {
979         m_liquid_transform_timer = 0.0;
980         m_print_info_timer = 0.0;
981         m_objectdata_timer = 0.0;
982         m_emergethread_trigger_timer = 0.0;
983         m_savemap_timer = 0.0;
984         
985         m_env_mutex.Init();
986         m_con_mutex.Init();
987         m_step_dtime_mutex.Init();
988         m_step_dtime = 0.0;
989
990         m_env.getMap().addEventReceiver(this);
991
992         // Load players
993         m_env.deSerializePlayers(m_mapsavedir);
994 }
995
996 Server::~Server()
997 {
998         dstream<<"Server::~Server()"<<std::endl;
999
1000         /*
1001                 Send shutdown message
1002         */
1003         {
1004                 JMutexAutoLock conlock(m_con_mutex);
1005                 
1006                 std::wstring line = L"*** Server shutting down";
1007
1008                 /*
1009                         Send the message to clients
1010                 */
1011                 for(core::map<u16, RemoteClient*>::Iterator
1012                         i = m_clients.getIterator();
1013                         i.atEnd() == false; i++)
1014                 {
1015                         // Get client and check that it is valid
1016                         RemoteClient *client = i.getNode()->getValue();
1017                         assert(client->peer_id == i.getNode()->getKey());
1018                         if(client->serialization_version == SER_FMT_VER_INVALID)
1019                                 continue;
1020
1021                         try{
1022                                 SendChatMessage(client->peer_id, line);
1023                         }
1024                         catch(con::PeerNotFoundException &e)
1025                         {}
1026                 }
1027         }
1028
1029         /*
1030                 Save players
1031         */
1032         dstream<<"Server: Saving players"<<std::endl;
1033         m_env.serializePlayers(m_mapsavedir);
1034         
1035         /*
1036                 Stop threads
1037         */
1038         stop();
1039         
1040         /*
1041                 Delete clients
1042         */
1043         {
1044                 JMutexAutoLock clientslock(m_con_mutex);
1045
1046                 for(core::map<u16, RemoteClient*>::Iterator
1047                         i = m_clients.getIterator();
1048                         i.atEnd() == false; i++)
1049                 {
1050                         /*// Delete player
1051                         // NOTE: These are removed by env destructor
1052                         {
1053                                 u16 peer_id = i.getNode()->getKey();
1054                                 JMutexAutoLock envlock(m_env_mutex);
1055                                 m_env.removePlayer(peer_id);
1056                         }*/
1057                         
1058                         // Delete client
1059                         delete i.getNode()->getValue();
1060                 }
1061         }
1062 }
1063
1064 void Server::start(unsigned short port)
1065 {
1066         DSTACK(__FUNCTION_NAME);
1067         // Stop thread if already running
1068         m_thread.stop();
1069         
1070         // Initialize connection
1071         m_con.setTimeoutMs(30);
1072         m_con.Serve(port);
1073
1074         // Start thread
1075         m_thread.setRun(true);
1076         m_thread.Start();
1077         
1078         dout_server<<"Server: Started on port "<<port<<std::endl;
1079 }
1080
1081 void Server::stop()
1082 {
1083         DSTACK(__FUNCTION_NAME);
1084
1085         // Stop threads (set run=false first so both start stopping)
1086         m_thread.setRun(false);
1087         m_emergethread.setRun(false);
1088         m_thread.stop();
1089         m_emergethread.stop();
1090         
1091         dout_server<<"Server: Threads stopped"<<std::endl;
1092 }
1093
1094 void Server::step(float dtime)
1095 {
1096         DSTACK(__FUNCTION_NAME);
1097         // Limit a bit
1098         if(dtime > 2.0)
1099                 dtime = 2.0;
1100         {
1101                 JMutexAutoLock lock(m_step_dtime_mutex);
1102                 m_step_dtime += dtime;
1103         }
1104 }
1105
1106 void Server::AsyncRunStep()
1107 {
1108         DSTACK(__FUNCTION_NAME);
1109         
1110         float dtime;
1111         {
1112                 JMutexAutoLock lock1(m_step_dtime_mutex);
1113                 dtime = m_step_dtime;
1114         }
1115         
1116         // Send blocks to clients
1117         SendBlocks(dtime);
1118         
1119         if(dtime < 0.001)
1120                 return;
1121         
1122         //dstream<<"Server steps "<<dtime<<std::endl;
1123         //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1124         
1125         {
1126                 JMutexAutoLock lock1(m_step_dtime_mutex);
1127                 m_step_dtime -= dtime;
1128         }
1129
1130         /*
1131                 Update uptime
1132         */
1133         {
1134                 m_uptime.set(m_uptime.get() + dtime);
1135         }
1136         
1137         /*
1138                 Update m_time_of_day
1139         */
1140         {
1141                 m_time_counter += dtime;
1142                 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1143                 u32 units = (u32)(m_time_counter*speed);
1144                 m_time_counter -= (f32)units / speed;
1145                 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1146                 
1147                 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1148
1149                 /*
1150                         Send to clients at constant intervals
1151                 */
1152
1153                 m_time_of_day_send_timer -= dtime;
1154                 if(m_time_of_day_send_timer < 0.0)
1155                 {
1156                         m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1157
1158                         //JMutexAutoLock envlock(m_env_mutex);
1159                         JMutexAutoLock conlock(m_con_mutex);
1160
1161                         for(core::map<u16, RemoteClient*>::Iterator
1162                                 i = m_clients.getIterator();
1163                                 i.atEnd() == false; i++)
1164                         {
1165                                 RemoteClient *client = i.getNode()->getValue();
1166                                 //Player *player = m_env.getPlayer(client->peer_id);
1167                                 
1168                                 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1169                                                 m_time_of_day.get());
1170                                 // Send as reliable
1171                                 m_con.Send(client->peer_id, 0, data, true);
1172                         }
1173                 }
1174         }
1175
1176         {
1177                 // Process connection's timeouts
1178                 JMutexAutoLock lock2(m_con_mutex);
1179                 m_con.RunTimeouts(dtime);
1180         }
1181         
1182         {
1183                 // This has to be called so that the client list gets synced
1184                 // with the peer list of the connection
1185                 handlePeerChanges();
1186         }
1187
1188         {
1189                 // Step environment
1190                 // This also runs Map's timers
1191                 JMutexAutoLock lock(m_env_mutex);
1192                 m_env.step(dtime);
1193         }
1194         
1195         /*
1196                 Do background stuff
1197         */
1198         
1199         /*
1200                 Transform liquids
1201         */
1202         m_liquid_transform_timer += dtime;
1203         if(m_liquid_transform_timer >= 1.00)
1204         {
1205                 m_liquid_transform_timer -= 1.00;
1206                 
1207                 JMutexAutoLock lock(m_env_mutex);
1208                 
1209                 core::map<v3s16, MapBlock*> modified_blocks;
1210                 m_env.getMap().transformLiquids(modified_blocks);
1211 #if 0           
1212                 /*
1213                         Update lighting
1214                 */
1215                 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1216                 ServerMap &map = ((ServerMap&)m_env.getMap());
1217                 map.updateLighting(modified_blocks, lighting_modified_blocks);
1218                 
1219                 // Add blocks modified by lighting to modified_blocks
1220                 for(core::map<v3s16, MapBlock*>::Iterator
1221                                 i = lighting_modified_blocks.getIterator();
1222                                 i.atEnd() == false; i++)
1223                 {
1224                         MapBlock *block = i.getNode()->getValue();
1225                         modified_blocks.insert(block->getPos(), block);
1226                 }
1227 #endif
1228                 /*
1229                         Set the modified blocks unsent for all the clients
1230                 */
1231                 
1232                 JMutexAutoLock lock2(m_con_mutex);
1233
1234                 for(core::map<u16, RemoteClient*>::Iterator
1235                                 i = m_clients.getIterator();
1236                                 i.atEnd() == false; i++)
1237                 {
1238                         RemoteClient *client = i.getNode()->getValue();
1239                         
1240                         if(modified_blocks.size() > 0)
1241                         {
1242                                 // Remove block from sent history
1243                                 client->SetBlocksNotSent(modified_blocks);
1244                         }
1245                 }
1246         }
1247
1248         // Periodically print some info
1249         {
1250                 float &counter = m_print_info_timer;
1251                 counter += dtime;
1252                 if(counter >= 30.0)
1253                 {
1254                         counter = 0.0;
1255
1256                         JMutexAutoLock lock2(m_con_mutex);
1257
1258                         for(core::map<u16, RemoteClient*>::Iterator
1259                                 i = m_clients.getIterator();
1260                                 i.atEnd() == false; i++)
1261                         {
1262                                 //u16 peer_id = i.getNode()->getKey();
1263                                 RemoteClient *client = i.getNode()->getValue();
1264                                 Player *player = m_env.getPlayer(client->peer_id);
1265                                 if(player==NULL)
1266                                         continue;
1267                                 std::cout<<player->getName()<<"\t";
1268                                 client->PrintInfo(std::cout);
1269                         }
1270                 }
1271         }
1272
1273         //if(g_settings.getBool("enable_experimental"))
1274         {
1275
1276         /*
1277                 Check added and deleted active objects
1278         */
1279         {
1280                 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1281
1282                 JMutexAutoLock envlock(m_env_mutex);
1283                 JMutexAutoLock conlock(m_con_mutex);
1284                 
1285                 // Radius inside which objects are active
1286                 s16 radius = 32;
1287
1288                 for(core::map<u16, RemoteClient*>::Iterator
1289                         i = m_clients.getIterator();
1290                         i.atEnd() == false; i++)
1291                 {
1292                         RemoteClient *client = i.getNode()->getValue();
1293                         Player *player = m_env.getPlayer(client->peer_id);
1294                         if(player==NULL)
1295                         {
1296                                 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1297                                                 <<" has no associated player"<<std::endl;
1298                                 continue;
1299                         }
1300                         v3s16 pos = floatToInt(player->getPosition(), BS);
1301
1302                         core::map<u16, bool> removed_objects;
1303                         core::map<u16, bool> added_objects;
1304                         m_env.getRemovedActiveObjects(pos, radius,
1305                                         client->m_known_objects, removed_objects);
1306                         m_env.getAddedActiveObjects(pos, radius,
1307                                         client->m_known_objects, added_objects);
1308                         
1309                         // Ignore if nothing happened
1310                         if(removed_objects.size() == 0 && added_objects.size() == 0)
1311                         {
1312                                 //dstream<<"INFO: active objects: none changed"<<std::endl;
1313                                 continue;
1314                         }
1315                         
1316                         std::string data_buffer;
1317
1318                         char buf[4];
1319                         
1320                         // Handle removed objects
1321                         writeU16((u8*)buf, removed_objects.size());
1322                         data_buffer.append(buf, 2);
1323                         for(core::map<u16, bool>::Iterator
1324                                         i = removed_objects.getIterator();
1325                                         i.atEnd()==false; i++)
1326                         {
1327                                 // Get object
1328                                 u16 id = i.getNode()->getKey();
1329                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1330
1331                                 // Add to data buffer for sending
1332                                 writeU16((u8*)buf, i.getNode()->getKey());
1333                                 data_buffer.append(buf, 2);
1334                                 
1335                                 // Remove from known objects
1336                                 client->m_known_objects.remove(i.getNode()->getKey());
1337
1338                                 if(obj && obj->m_known_by_count > 0)
1339                                         obj->m_known_by_count--;
1340                         }
1341
1342                         // Handle added objects
1343                         writeU16((u8*)buf, added_objects.size());
1344                         data_buffer.append(buf, 2);
1345                         for(core::map<u16, bool>::Iterator
1346                                         i = added_objects.getIterator();
1347                                         i.atEnd()==false; i++)
1348                         {
1349                                 // Get object
1350                                 u16 id = i.getNode()->getKey();
1351                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1352                                 
1353                                 // Get object type
1354                                 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1355                                 if(obj == NULL)
1356                                         dstream<<"WARNING: "<<__FUNCTION_NAME
1357                                                         <<": NULL object"<<std::endl;
1358                                 else
1359                                         type = obj->getType();
1360
1361                                 // Add to data buffer for sending
1362                                 writeU16((u8*)buf, id);
1363                                 data_buffer.append(buf, 2);
1364                                 writeU8((u8*)buf, type);
1365                                 data_buffer.append(buf, 1);
1366                                 
1367                                 if(obj)
1368                                         data_buffer.append(serializeLongString(
1369                                                         obj->getClientInitializationData()));
1370                                 else
1371                                         data_buffer.append(serializeLongString(""));
1372
1373                                 // Add to known objects
1374                                 client->m_known_objects.insert(i.getNode()->getKey(), false);
1375
1376                                 if(obj)
1377                                         obj->m_known_by_count++;
1378                         }
1379
1380                         // Send packet
1381                         SharedBuffer<u8> reply(2 + data_buffer.size());
1382                         writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1383                         memcpy((char*)&reply[2], data_buffer.c_str(),
1384                                         data_buffer.size());
1385                         // Send as reliable
1386                         m_con.Send(client->peer_id, 0, reply, true);
1387
1388                         dstream<<"INFO: Server: Sent object remove/add: "
1389                                         <<removed_objects.size()<<" removed, "
1390                                         <<added_objects.size()<<" added, "
1391                                         <<"packet size is "<<reply.getSize()<<std::endl;
1392                 }
1393
1394 #if 0
1395                 /*
1396                         Collect a list of all the objects known by the clients
1397                         and report it back to the environment.
1398                 */
1399
1400                 core::map<u16, bool> all_known_objects;
1401
1402                 for(core::map<u16, RemoteClient*>::Iterator
1403                         i = m_clients.getIterator();
1404                         i.atEnd() == false; i++)
1405                 {
1406                         RemoteClient *client = i.getNode()->getValue();
1407                         // Go through all known objects of client
1408                         for(core::map<u16, bool>::Iterator
1409                                         i = client->m_known_objects.getIterator();
1410                                         i.atEnd()==false; i++)
1411                         {
1412                                 u16 id = i.getNode()->getKey();
1413                                 all_known_objects[id] = true;
1414                         }
1415                 }
1416                 
1417                 m_env.setKnownActiveObjects(whatever);
1418 #endif
1419
1420         }
1421
1422         /*
1423                 Send object messages
1424         */
1425         {
1426                 JMutexAutoLock envlock(m_env_mutex);
1427                 JMutexAutoLock conlock(m_con_mutex);
1428
1429                 // Key = object id
1430                 // Value = data sent by object
1431                 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1432
1433                 // Get active object messages from environment
1434                 for(;;)
1435                 {
1436                         ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1437                         if(aom.id == 0)
1438                                 break;
1439                         
1440                         core::list<ActiveObjectMessage>* message_list = NULL;
1441                         core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1442                         n = buffered_messages.find(aom.id);
1443                         if(n == NULL)
1444                         {
1445                                 message_list = new core::list<ActiveObjectMessage>;
1446                                 buffered_messages.insert(aom.id, message_list);
1447                         }
1448                         else
1449                         {
1450                                 message_list = n->getValue();
1451                         }
1452                         message_list->push_back(aom);
1453                 }
1454                 
1455                 // Route data to every client
1456                 for(core::map<u16, RemoteClient*>::Iterator
1457                         i = m_clients.getIterator();
1458                         i.atEnd()==false; i++)
1459                 {
1460                         RemoteClient *client = i.getNode()->getValue();
1461                         std::string reliable_data;
1462                         std::string unreliable_data;
1463                         // Go through all objects in message buffer
1464                         for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1465                                         j = buffered_messages.getIterator();
1466                                         j.atEnd()==false; j++)
1467                         {
1468                                 // If object is not known by client, skip it
1469                                 u16 id = j.getNode()->getKey();
1470                                 if(client->m_known_objects.find(id) == NULL)
1471                                         continue;
1472                                 // Get message list of object
1473                                 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1474                                 // Go through every message
1475                                 for(core::list<ActiveObjectMessage>::Iterator
1476                                                 k = list->begin(); k != list->end(); k++)
1477                                 {
1478                                         // Compose the full new data with header
1479                                         ActiveObjectMessage aom = *k;
1480                                         std::string new_data;
1481                                         // Add object id
1482                                         char buf[2];
1483                                         writeU16((u8*)&buf[0], aom.id);
1484                                         new_data.append(buf, 2);
1485                                         // Add data
1486                                         new_data += serializeString(aom.datastring);
1487                                         // Add data to buffer
1488                                         if(aom.reliable)
1489                                                 reliable_data += new_data;
1490                                         else
1491                                                 unreliable_data += new_data;
1492                                 }
1493                         }
1494                         /*
1495                                 reliable_data and unreliable_data are now ready.
1496                                 Send them.
1497                         */
1498                         if(reliable_data.size() > 0)
1499                         {
1500                                 SharedBuffer<u8> reply(2 + reliable_data.size());
1501                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1502                                 memcpy((char*)&reply[2], reliable_data.c_str(),
1503                                                 reliable_data.size());
1504                                 // Send as reliable
1505                                 m_con.Send(client->peer_id, 0, reply, true);
1506                         }
1507                         if(unreliable_data.size() > 0)
1508                         {
1509                                 SharedBuffer<u8> reply(2 + unreliable_data.size());
1510                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1511                                 memcpy((char*)&reply[2], unreliable_data.c_str(),
1512                                                 unreliable_data.size());
1513                                 // Send as unreliable
1514                                 m_con.Send(client->peer_id, 0, reply, false);
1515                         }
1516
1517                         /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1518                         {
1519                                 dstream<<"INFO: Server: Size of object message data: "
1520                                                 <<"reliable: "<<reliable_data.size()
1521                                                 <<", unreliable: "<<unreliable_data.size()
1522                                                 <<std::endl;
1523                         }*/
1524                 }
1525
1526                 // Clear buffered_messages
1527                 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1528                                 i = buffered_messages.getIterator();
1529                                 i.atEnd()==false; i++)
1530                 {
1531                         delete i.getNode()->getValue();
1532                 }
1533         }
1534
1535         } // enable_experimental
1536
1537         /*
1538                 Send queued-for-sending map edit events.
1539         */
1540         {
1541                 while(m_unsent_map_edit_queue.size() != 0)
1542                 {
1543                         MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1544
1545                         if(event->type == MEET_ADDNODE)
1546                         {
1547                                 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1548                                 sendAddNode(event->p, event->n, event->already_known_by_peer);
1549                         }
1550                         else if(event->type == MEET_REMOVENODE)
1551                         {
1552                                 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1553                                 sendRemoveNode(event->p, event->already_known_by_peer);
1554                         }
1555                         else if(event->type == MEET_OTHER)
1556                         {
1557                                 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1558                                                 <<std::endl;
1559                         }
1560                         else
1561                         {
1562                                 dstream<<"WARNING: Server: Unknown MapEditEvent "
1563                                                 <<((u32)event->type)<<std::endl;
1564                         }
1565
1566                         delete event;
1567                 }
1568         }
1569
1570         /*
1571                 Send object positions
1572                 TODO: Get rid of MapBlockObjects
1573         */
1574         {
1575                 float &counter = m_objectdata_timer;
1576                 counter += dtime;
1577                 if(counter >= g_settings.getFloat("objectdata_interval"))
1578                 {
1579                         JMutexAutoLock lock1(m_env_mutex);
1580                         JMutexAutoLock lock2(m_con_mutex);
1581                         SendObjectData(counter);
1582
1583                         counter = 0.0;
1584                 }
1585         }
1586         
1587         /*
1588                 Step node metadata
1589         */
1590         {
1591                 //TimeTaker timer("Step node metadata");
1592
1593                 JMutexAutoLock envlock(m_env_mutex);
1594                 JMutexAutoLock conlock(m_con_mutex);
1595                 
1596                 core::map<v3s16, MapBlock*> changed_blocks;
1597                 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1598
1599                 for(core::map<v3s16, MapBlock*>::Iterator
1600                                 i = changed_blocks.getIterator();
1601                                 i.atEnd() == false; i++)
1602                 {
1603                         MapBlock *block = i.getNode()->getValue();
1604
1605                         for(core::map<u16, RemoteClient*>::Iterator
1606                                 i = m_clients.getIterator();
1607                                 i.atEnd()==false; i++)
1608                         {
1609                                 RemoteClient *client = i.getNode()->getValue();
1610                                 client->SetBlockNotSent(block->getPos());
1611                         }
1612                 }
1613         }
1614                 
1615         /*
1616                 Trigger emergethread (it somehow gets to a non-triggered but
1617                 bysy state sometimes)
1618         */
1619         {
1620                 float &counter = m_emergethread_trigger_timer;
1621                 counter += dtime;
1622                 if(counter >= 2.0)
1623                 {
1624                         counter = 0.0;
1625                         
1626                         m_emergethread.trigger();
1627                 }
1628         }
1629
1630         // Save map
1631         {
1632                 float &counter = m_savemap_timer;
1633                 counter += dtime;
1634                 if(counter >= g_settings.getFloat("server_map_save_interval"))
1635                 {
1636                         counter = 0.0;
1637
1638                         JMutexAutoLock lock(m_env_mutex);
1639
1640                         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1641                         {
1642                                 // Save only changed parts
1643                                 m_env.getMap().save(true);
1644
1645                                 // Delete unused sectors
1646                                 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1647                                                 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1648                                 if(deleted_count > 0)
1649                                 {
1650                                         dout_server<<"Server: Unloaded "<<deleted_count
1651                                                         <<" sectors from memory"<<std::endl;
1652                                 }
1653
1654                                 // Save players
1655                                 m_env.serializePlayers(m_mapsavedir);
1656                         }
1657                 }
1658         }
1659 }
1660
1661 void Server::Receive()
1662 {
1663         DSTACK(__FUNCTION_NAME);
1664         u32 data_maxsize = 10000;
1665         Buffer<u8> data(data_maxsize);
1666         u16 peer_id;
1667         u32 datasize;
1668         try{
1669                 {
1670                         JMutexAutoLock conlock(m_con_mutex);
1671                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1672                 }
1673
1674                 // This has to be called so that the client list gets synced
1675                 // with the peer list of the connection
1676                 handlePeerChanges();
1677
1678                 ProcessData(*data, datasize, peer_id);
1679         }
1680         catch(con::InvalidIncomingDataException &e)
1681         {
1682                 derr_server<<"Server::Receive(): "
1683                                 "InvalidIncomingDataException: what()="
1684                                 <<e.what()<<std::endl;
1685         }
1686         catch(con::PeerNotFoundException &e)
1687         {
1688                 //NOTE: This is not needed anymore
1689                 
1690                 // The peer has been disconnected.
1691                 // Find the associated player and remove it.
1692
1693                 /*JMutexAutoLock envlock(m_env_mutex);
1694
1695                 dout_server<<"ServerThread: peer_id="<<peer_id
1696                                 <<" has apparently closed connection. "
1697                                 <<"Removing player."<<std::endl;
1698
1699                 m_env.removePlayer(peer_id);*/
1700         }
1701 }
1702
1703 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1704 {
1705         DSTACK(__FUNCTION_NAME);
1706         // Environment is locked first.
1707         JMutexAutoLock envlock(m_env_mutex);
1708         JMutexAutoLock conlock(m_con_mutex);
1709         
1710         con::Peer *peer;
1711         try{
1712                 peer = m_con.GetPeer(peer_id);
1713         }
1714         catch(con::PeerNotFoundException &e)
1715         {
1716                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1717                                 <<peer_id<<" not found"<<std::endl;
1718                 return;
1719         }
1720         
1721         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1722
1723         try
1724         {
1725
1726         if(datasize < 2)
1727                 return;
1728
1729         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1730         
1731         if(command == TOSERVER_INIT)
1732         {
1733                 // [0] u16 TOSERVER_INIT
1734                 // [2] u8 SER_FMT_VER_HIGHEST
1735                 // [3] u8[20] player_name
1736
1737                 if(datasize < 3)
1738                         return;
1739
1740                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1741                                 <<peer->id<<std::endl;
1742
1743                 // First byte after command is maximum supported
1744                 // serialization version
1745                 u8 client_max = data[2];
1746                 u8 our_max = SER_FMT_VER_HIGHEST;
1747                 // Use the highest version supported by both
1748                 u8 deployed = core::min_(client_max, our_max);
1749                 // If it's lower than the lowest supported, give up.
1750                 if(deployed < SER_FMT_VER_LOWEST)
1751                         deployed = SER_FMT_VER_INVALID;
1752
1753                 //peer->serialization_version = deployed;
1754                 getClient(peer->id)->pending_serialization_version = deployed;
1755
1756                 if(deployed == SER_FMT_VER_INVALID)
1757                 {
1758                         derr_server<<DTIME<<"Server: Cannot negotiate "
1759                                         "serialization version with peer "
1760                                         <<peer_id<<std::endl;
1761                         return;
1762                 }
1763
1764                 /*
1765                         Set up player
1766                 */
1767                 
1768                 // Get player name
1769                 const u32 playername_size = 20;
1770                 char playername[playername_size];
1771                 for(u32 i=0; i<playername_size-1; i++)
1772                 {
1773                         playername[i] = data[3+i];
1774                 }
1775                 playername[playername_size-1] = 0;
1776                 
1777                 // Get player
1778                 Player *player = emergePlayer(playername, "", peer_id);
1779                 //Player *player = m_env.getPlayer(peer_id);
1780
1781                 /*{
1782                         // DEBUG: Test serialization
1783                         std::ostringstream test_os;
1784                         player->serialize(test_os);
1785                         dstream<<"Player serialization test: \""<<test_os.str()
1786                                         <<"\""<<std::endl;
1787                         std::istringstream test_is(test_os.str());
1788                         player->deSerialize(test_is);
1789                 }*/
1790
1791                 // If failed, cancel
1792                 if(player == NULL)
1793                 {
1794                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1795                                         <<": failed to emerge player"<<std::endl;
1796                         return;
1797                 }
1798
1799                 /*
1800                 // If a client is already connected to the player, cancel
1801                 if(player->peer_id != 0)
1802                 {
1803                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1804                                         <<" tried to connect to "
1805                                         "an already connected player (peer_id="
1806                                         <<player->peer_id<<")"<<std::endl;
1807                         return;
1808                 }
1809                 // Set client of player
1810                 player->peer_id = peer_id;
1811                 */
1812
1813                 // Check if player doesn't exist
1814                 if(player == NULL)
1815                         throw con::InvalidIncomingDataException
1816                                 ("Server::ProcessData(): INIT: Player doesn't exist");
1817
1818                 /*// update name if it was supplied
1819                 if(datasize >= 20+3)
1820                 {
1821                         data[20+3-1] = 0;
1822                         player->updateName((const char*)&data[3]);
1823                 }*/
1824                 
1825                 /*
1826                         Answer with a TOCLIENT_INIT
1827                 */
1828                 {
1829                         SharedBuffer<u8> reply(2+1+6+8);
1830                         writeU16(&reply[0], TOCLIENT_INIT);
1831                         writeU8(&reply[2], deployed);
1832                         writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1833                         //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1834                         writeU64(&reply[2+1+6], 0); // no seed
1835                         
1836                         // Send as reliable
1837                         m_con.Send(peer_id, 0, reply, true);
1838                 }
1839
1840                 /*
1841                         Send complete position information
1842                 */
1843                 SendMovePlayer(player);
1844
1845                 return;
1846         }
1847
1848         if(command == TOSERVER_INIT2)
1849         {
1850                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1851                                 <<peer->id<<std::endl;
1852
1853
1854                 getClient(peer->id)->serialization_version
1855                                 = getClient(peer->id)->pending_serialization_version;
1856
1857                 /*
1858                         Send some initialization data
1859                 */
1860                 
1861                 // Send player info to all players
1862                 SendPlayerInfos();
1863
1864                 // Send inventory to player
1865                 UpdateCrafting(peer->id);
1866                 SendInventory(peer->id);
1867
1868                 // Send HP
1869                 {
1870                         Player *player = m_env.getPlayer(peer_id);
1871                         SendPlayerHP(player);
1872                 }
1873                 
1874                 // Send time of day
1875                 {
1876                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1877                                         m_time_of_day.get());
1878                         m_con.Send(peer->id, 0, data, true);
1879                 }
1880                 
1881                 // Send information about server to player in chat
1882                 SendChatMessage(peer_id, getStatusString());
1883                 
1884                 // Send information about joining in chat
1885                 {
1886                         std::wstring name = L"unknown";
1887                         Player *player = m_env.getPlayer(peer_id);
1888                         if(player != NULL)
1889                                 name = narrow_to_wide(player->getName());
1890                         
1891                         std::wstring message;
1892                         message += L"*** ";
1893                         message += name;
1894                         message += L" joined game";
1895                         BroadcastChatMessage(message);
1896                 }
1897
1898                 return;
1899         }
1900
1901         if(peer_ser_ver == SER_FMT_VER_INVALID)
1902         {
1903                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1904                                 " serialization format invalid or not initialized."
1905                                 " Skipping incoming command="<<command<<std::endl;
1906                 return;
1907         }
1908         
1909         Player *player = m_env.getPlayer(peer_id);
1910
1911         if(player == NULL){
1912                 derr_server<<"Server::ProcessData(): Cancelling: "
1913                                 "No player for peer_id="<<peer_id
1914                                 <<std::endl;
1915                 return;
1916         }
1917         if(command == TOSERVER_PLAYERPOS)
1918         {
1919                 if(datasize < 2+12+12+4+4)
1920                         return;
1921         
1922                 u32 start = 0;
1923                 v3s32 ps = readV3S32(&data[start+2]);
1924                 v3s32 ss = readV3S32(&data[start+2+12]);
1925                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1926                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1927                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1928                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1929                 pitch = wrapDegrees(pitch);
1930                 yaw = wrapDegrees(yaw);
1931                 player->setPosition(position);
1932                 player->setSpeed(speed);
1933                 player->setPitch(pitch);
1934                 player->setYaw(yaw);
1935                 
1936                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1937                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1938                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1939         }
1940         else if(command == TOSERVER_GOTBLOCKS)
1941         {
1942                 if(datasize < 2+1)
1943                         return;
1944                 
1945                 /*
1946                         [0] u16 command
1947                         [2] u8 count
1948                         [3] v3s16 pos_0
1949                         [3+6] v3s16 pos_1
1950                         ...
1951                 */
1952
1953                 u16 count = data[2];
1954                 for(u16 i=0; i<count; i++)
1955                 {
1956                         if((s16)datasize < 2+1+(i+1)*6)
1957                                 throw con::InvalidIncomingDataException
1958                                         ("GOTBLOCKS length is too short");
1959                         v3s16 p = readV3S16(&data[2+1+i*6]);
1960                         /*dstream<<"Server: GOTBLOCKS ("
1961                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1962                         RemoteClient *client = getClient(peer_id);
1963                         client->GotBlock(p);
1964                 }
1965         }
1966         else if(command == TOSERVER_DELETEDBLOCKS)
1967         {
1968                 if(datasize < 2+1)
1969                         return;
1970                 
1971                 /*
1972                         [0] u16 command
1973                         [2] u8 count
1974                         [3] v3s16 pos_0
1975                         [3+6] v3s16 pos_1
1976                         ...
1977                 */
1978
1979                 u16 count = data[2];
1980                 for(u16 i=0; i<count; i++)
1981                 {
1982                         if((s16)datasize < 2+1+(i+1)*6)
1983                                 throw con::InvalidIncomingDataException
1984                                         ("DELETEDBLOCKS length is too short");
1985                         v3s16 p = readV3S16(&data[2+1+i*6]);
1986                         /*dstream<<"Server: DELETEDBLOCKS ("
1987                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1988                         RemoteClient *client = getClient(peer_id);
1989                         client->SetBlockNotSent(p);
1990                 }
1991         }
1992         else if(command == TOSERVER_CLICK_OBJECT)
1993         {
1994                 if(datasize < 13)
1995                         return;
1996
1997                 /*
1998                         [0] u16 command
1999                         [2] u8 button (0=left, 1=right)
2000                         [3] v3s16 block
2001                         [9] s16 id
2002                         [11] u16 item
2003                 */
2004                 u8 button = readU8(&data[2]);
2005                 v3s16 p;
2006                 p.X = readS16(&data[3]);
2007                 p.Y = readS16(&data[5]);
2008                 p.Z = readS16(&data[7]);
2009                 s16 id = readS16(&data[9]);
2010                 //u16 item_i = readU16(&data[11]);
2011
2012                 MapBlock *block = NULL;
2013                 try
2014                 {
2015                         block = m_env.getMap().getBlockNoCreate(p);
2016                 }
2017                 catch(InvalidPositionException &e)
2018                 {
2019                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2020                         return;
2021                 }
2022
2023                 MapBlockObject *obj = block->getObject(id);
2024
2025                 if(obj == NULL)
2026                 {
2027                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2028                         return;
2029                 }
2030
2031                 //TODO: Check that object is reasonably close
2032                 
2033                 // Left click
2034                 if(button == 0)
2035                 {
2036                         InventoryList *ilist = player->inventory.getList("main");
2037                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2038                         {
2039                         
2040                                 // Skip if inventory has no free space
2041                                 if(ilist->getUsedSlots() == ilist->getSize())
2042                                 {
2043                                         dout_server<<"Player inventory has no free space"<<std::endl;
2044                                         return;
2045                                 }
2046                                 
2047                                 /*
2048                                         Create the inventory item
2049                                 */
2050                                 InventoryItem *item = NULL;
2051                                 // If it is an item-object, take the item from it
2052                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2053                                 {
2054                                         item = ((ItemObject*)obj)->createInventoryItem();
2055                                 }
2056                                 // Else create an item of the object
2057                                 else
2058                                 {
2059                                         item = new MapBlockObjectItem
2060                                                         (obj->getInventoryString());
2061                                 }
2062                                 
2063                                 // Add to inventory and send inventory
2064                                 ilist->addItem(item);
2065                                 UpdateCrafting(player->peer_id);
2066                                 SendInventory(player->peer_id);
2067                         }
2068
2069                         // Remove from block
2070                         block->removeObject(id);
2071                 }
2072         }
2073         else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2074         {
2075                 if(datasize < 7)
2076                         return;
2077
2078                 /*
2079                         length: 7
2080                         [0] u16 command
2081                         [2] u8 button (0=left, 1=right)
2082                         [3] u16 id
2083                         [5] u16 item
2084                 */
2085                 u8 button = readU8(&data[2]);
2086                 u16 id = readS16(&data[3]);
2087                 u16 item_i = readU16(&data[11]);
2088         
2089                 ServerActiveObject *obj = m_env.getActiveObject(id);
2090
2091                 if(obj == NULL)
2092                 {
2093                         derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2094                                         <<std::endl;
2095                         return;
2096                 }
2097
2098                 //TODO: Check that object is reasonably close
2099                 
2100                 // Left click, pick object up (usually)
2101                 if(button == 0)
2102                 {
2103                         InventoryList *ilist = player->inventory.getList("main");
2104                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2105                         {
2106                         
2107                                 // Skip if inventory has no free space
2108                                 if(ilist->getUsedSlots() == ilist->getSize())
2109                                 {
2110                                         dout_server<<"Player inventory has no free space"<<std::endl;
2111                                         return;
2112                                 }
2113
2114                                 // Skip if object has been removed
2115                                 if(obj->m_removed)
2116                                         return;
2117                                 
2118                                 /*
2119                                         Create the inventory item
2120                                 */
2121                                 InventoryItem *item = obj->createPickedUpItem();
2122                                 
2123                                 if(item)
2124                                 {
2125                                         // Add to inventory and send inventory
2126                                         ilist->addItem(item);
2127                                         UpdateCrafting(player->peer_id);
2128                                         SendInventory(player->peer_id);
2129
2130                                         // Remove object from environment
2131                                         obj->m_removed = true;
2132                                 }
2133                                 else
2134                                 {
2135                                         /*
2136                                                 Item cannot be picked up. Punch it instead.
2137                                         */
2138
2139                                         ToolItem *titem = NULL;
2140                                         std::string toolname = "";
2141
2142                                         InventoryList *mlist = player->inventory.getList("main");
2143                                         if(mlist != NULL)
2144                                         {
2145                                                 InventoryItem *item = mlist->getItem(item_i);
2146                                                 if(item && (std::string)item->getName() == "ToolItem")
2147                                                 {
2148                                                         titem = (ToolItem*)item;
2149                                                         toolname = titem->getToolName();
2150                                                 }
2151                                         }
2152                                         
2153                                         u16 wear = obj->punch(toolname);
2154                                         
2155                                         if(titem)
2156                                         {
2157                                                 bool weared_out = titem->addWear(wear);
2158                                                 if(weared_out)
2159                                                         mlist->deleteItem(item_i);
2160                                                 SendInventory(player->peer_id);
2161                                         }
2162                                 }
2163                         }
2164                 }
2165         }
2166         else if(command == TOSERVER_GROUND_ACTION)
2167         {
2168                 if(datasize < 17)
2169                         return;
2170                 /*
2171                         length: 17
2172                         [0] u16 command
2173                         [2] u8 action
2174                         [3] v3s16 nodepos_undersurface
2175                         [9] v3s16 nodepos_abovesurface
2176                         [15] u16 item
2177                         actions:
2178                         0: start digging
2179                         1: place block
2180                         2: stop digging (all parameters ignored)
2181                         3: digging completed
2182                 */
2183                 u8 action = readU8(&data[2]);
2184                 v3s16 p_under;
2185                 p_under.X = readS16(&data[3]);
2186                 p_under.Y = readS16(&data[5]);
2187                 p_under.Z = readS16(&data[7]);
2188                 v3s16 p_over;
2189                 p_over.X = readS16(&data[9]);
2190                 p_over.Y = readS16(&data[11]);
2191                 p_over.Z = readS16(&data[13]);
2192                 u16 item_i = readU16(&data[15]);
2193
2194                 //TODO: Check that target is reasonably close
2195                 
2196                 /*
2197                         0: start digging
2198                 */
2199                 if(action == 0)
2200                 {
2201                         /*
2202                                 NOTE: This can be used in the future to check if
2203                                 somebody is cheating, by checking the timing.
2204                         */
2205                 } // action == 0
2206
2207                 /*
2208                         2: stop digging
2209                 */
2210                 else if(action == 2)
2211                 {
2212 #if 0
2213                         RemoteClient *client = getClient(peer->id);
2214                         JMutexAutoLock digmutex(client->m_dig_mutex);
2215                         client->m_dig_tool_item = -1;
2216 #endif
2217                 }
2218
2219                 /*
2220                         3: Digging completed
2221                 */
2222                 else if(action == 3)
2223                 {
2224                         // Mandatory parameter; actually used for nothing
2225                         core::map<v3s16, MapBlock*> modified_blocks;
2226
2227                         u8 material;
2228                         u8 mineral = MINERAL_NONE;
2229
2230                         bool cannot_remove_node = false;
2231
2232                         try
2233                         {
2234                                 MapNode n = m_env.getMap().getNode(p_under);
2235                                 // Get mineral
2236                                 mineral = n.getMineral();
2237                                 // Get material at position
2238                                 material = n.d;
2239                                 // If not yet cancelled
2240                                 if(cannot_remove_node == false)
2241                                 {
2242                                         // If it's not diggable, do nothing
2243                                         if(content_diggable(material) == false)
2244                                         {
2245                                                 derr_server<<"Server: Not finishing digging: "
2246                                                                 <<"Node not diggable"
2247                                                                 <<std::endl;
2248                                                 cannot_remove_node = true;
2249                                         }
2250                                 }
2251                                 // If not yet cancelled
2252                                 if(cannot_remove_node == false)
2253                                 {
2254                                         // Get node metadata
2255                                         NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2256                                         if(meta && meta->nodeRemovalDisabled() == true)
2257                                         {
2258                                                 derr_server<<"Server: Not finishing digging: "
2259                                                                 <<"Node metadata disables removal"
2260                                                                 <<std::endl;
2261                                                 cannot_remove_node = true;
2262                                         }
2263                                 }
2264                         }
2265                         catch(InvalidPositionException &e)
2266                         {
2267                                 derr_server<<"Server: Not finishing digging: Node not found."
2268                                                 <<" Adding block to emerge queue."
2269                                                 <<std::endl;
2270                                 m_emerge_queue.addBlock(peer_id,
2271                                                 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2272                                 cannot_remove_node = true;
2273                         }
2274
2275                         /*
2276                                 If node can't be removed, set block to be re-sent to
2277                                 client and quit.
2278                         */
2279                         if(cannot_remove_node)
2280                         {
2281                                 derr_server<<"Server: Not finishing digging."<<std::endl;
2282
2283                                 // Client probably has wrong data.
2284                                 // Set block not sent, so that client will get
2285                                 // a valid one.
2286                                 dstream<<"Client "<<peer_id<<" tried to dig "
2287                                                 <<"node; but node cannot be removed."
2288                                                 <<" setting MapBlock not sent."<<std::endl;
2289                                 RemoteClient *client = getClient(peer_id);
2290                                 v3s16 blockpos = getNodeBlockPos(p_under);
2291                                 client->SetBlockNotSent(blockpos);
2292                                         
2293                                 return;
2294                         }
2295                         
2296                         /*
2297                                 Send the removal to all other clients.
2298                                 - If other player is close, send REMOVENODE
2299                                 - Otherwise set blocks not sent
2300                         */
2301                         core::list<u16> far_players;
2302                         sendRemoveNode(p_under, peer_id, &far_players, 100);
2303                         
2304                         /*
2305                                 Update and send inventory
2306                         */
2307
2308                         if(g_settings.getBool("creative_mode") == false)
2309                         {
2310                                 /*
2311                                         Wear out tool
2312                                 */
2313                                 InventoryList *mlist = player->inventory.getList("main");
2314                                 if(mlist != NULL)
2315                                 {
2316                                         InventoryItem *item = mlist->getItem(item_i);
2317                                         if(item && (std::string)item->getName() == "ToolItem")
2318                                         {
2319                                                 ToolItem *titem = (ToolItem*)item;
2320                                                 std::string toolname = titem->getToolName();
2321
2322                                                 // Get digging properties for material and tool
2323                                                 DiggingProperties prop =
2324                                                                 getDiggingProperties(material, toolname);
2325
2326                                                 if(prop.diggable == false)
2327                                                 {
2328                                                         derr_server<<"Server: WARNING: Player digged"
2329                                                                         <<" with impossible material + tool"
2330                                                                         <<" combination"<<std::endl;
2331                                                 }
2332                                                 
2333                                                 bool weared_out = titem->addWear(prop.wear);
2334
2335                                                 if(weared_out)
2336                                                 {
2337                                                         mlist->deleteItem(item_i);
2338                                                 }
2339                                         }
2340                                 }
2341
2342                                 /*
2343                                         Add dug item to inventory
2344                                 */
2345
2346                                 InventoryItem *item = NULL;
2347
2348                                 if(mineral != MINERAL_NONE)
2349                                         item = getDiggedMineralItem(mineral);
2350                                 
2351                                 // If not mineral
2352                                 if(item == NULL)
2353                                 {
2354                                         std::string &dug_s = content_features(material).dug_item;
2355                                         if(dug_s != "")
2356                                         {
2357                                                 std::istringstream is(dug_s, std::ios::binary);
2358                                                 item = InventoryItem::deSerialize(is);
2359                                         }
2360                                 }
2361                                 
2362                                 if(item != NULL)
2363                                 {
2364                                         // Add a item to inventory
2365                                         player->inventory.addItem("main", item);
2366
2367                                         // Send inventory
2368                                         UpdateCrafting(player->peer_id);
2369                                         SendInventory(player->peer_id);
2370                                 }
2371                         }
2372
2373                         /*
2374                                 Remove the node
2375                                 (this takes some time so it is done after the quick stuff)
2376                         */
2377                         m_ignore_map_edit_events = true;
2378                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2379                         m_ignore_map_edit_events = false;
2380                         
2381                         /*
2382                                 Set blocks not sent to far players
2383                         */
2384                         for(core::list<u16>::Iterator
2385                                         i = far_players.begin();
2386                                         i != far_players.end(); i++)
2387                         {
2388                                 u16 peer_id = *i;
2389                                 RemoteClient *client = getClient(peer_id);
2390                                 if(client==NULL)
2391                                         continue;
2392                                 client->SetBlocksNotSent(modified_blocks);
2393                         }
2394                 }
2395                 
2396                 /*
2397                         1: place block
2398                 */
2399                 else if(action == 1)
2400                 {
2401
2402                         InventoryList *ilist = player->inventory.getList("main");
2403                         if(ilist == NULL)
2404                                 return;
2405
2406                         // Get item
2407                         InventoryItem *item = ilist->getItem(item_i);
2408                         
2409                         // If there is no item, it is not possible to add it anywhere
2410                         if(item == NULL)
2411                                 return;
2412                         
2413                         /*
2414                                 Handle material items
2415                         */
2416                         if(std::string("MaterialItem") == item->getName())
2417                         {
2418                                 try{
2419                                         // Don't add a node if this is not a free space
2420                                         MapNode n2 = m_env.getMap().getNode(p_over);
2421                                         if(content_buildable_to(n2.d) == false)
2422                                         {
2423                                                 // Client probably has wrong data.
2424                                                 // Set block not sent, so that client will get
2425                                                 // a valid one.
2426                                                 dstream<<"Client "<<peer_id<<" tried to place"
2427                                                                 <<" node in invalid position; setting"
2428                                                                 <<" MapBlock not sent."<<std::endl;
2429                                                 RemoteClient *client = getClient(peer_id);
2430                                                 v3s16 blockpos = getNodeBlockPos(p_over);
2431                                                 client->SetBlockNotSent(blockpos);
2432                                                 return;
2433                                         }
2434                                 }
2435                                 catch(InvalidPositionException &e)
2436                                 {
2437                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2438                                                         <<" Adding block to emerge queue."
2439                                                         <<std::endl;
2440                                         m_emerge_queue.addBlock(peer_id,
2441                                                         getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2442                                         return;
2443                                 }
2444
2445                                 // Reset build time counter
2446                                 getClient(peer->id)->m_time_from_building = 0.0;
2447                                 
2448                                 // Create node data
2449                                 MaterialItem *mitem = (MaterialItem*)item;
2450                                 MapNode n;
2451                                 n.d = mitem->getMaterial();
2452                                 if(content_features(n.d).wall_mounted)
2453                                         n.dir = packDir(p_under - p_over);
2454                                 
2455                                 /*
2456                                         Send to all players
2457                                 */
2458                                 core::list<u16> far_players;
2459                                 sendAddNode(p_over, n, 0, &far_players, 100);
2460                                 
2461                                 /*
2462                                         Handle inventory
2463                                 */
2464                                 InventoryList *ilist = player->inventory.getList("main");
2465                                 if(g_settings.getBool("creative_mode") == false && ilist)
2466                                 {
2467                                         // Remove from inventory and send inventory
2468                                         if(mitem->getCount() == 1)
2469                                                 ilist->deleteItem(item_i);
2470                                         else
2471                                                 mitem->remove(1);
2472                                         // Send inventory
2473                                         UpdateCrafting(peer_id);
2474                                         SendInventory(peer_id);
2475                                 }
2476                                 
2477                                 /*
2478                                         Add node.
2479
2480                                         This takes some time so it is done after the quick stuff
2481                                 */
2482                                 core::map<v3s16, MapBlock*> modified_blocks;
2483                                 m_ignore_map_edit_events = true;
2484                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2485                                 m_ignore_map_edit_events = false;
2486                                 
2487                                 /*
2488                                         Set blocks not sent to far players
2489                                 */
2490                                 for(core::list<u16>::Iterator
2491                                                 i = far_players.begin();
2492                                                 i != far_players.end(); i++)
2493                                 {
2494                                         u16 peer_id = *i;
2495                                         RemoteClient *client = getClient(peer_id);
2496                                         if(client==NULL)
2497                                                 continue;
2498                                         client->SetBlocksNotSent(modified_blocks);
2499                                 }
2500
2501                                 /*
2502                                         Calculate special events
2503                                 */
2504                                 
2505                                 /*if(n.d == CONTENT_MESE)
2506                                 {
2507                                         u32 count = 0;
2508                                         for(s16 z=-1; z<=1; z++)
2509                                         for(s16 y=-1; y<=1; y++)
2510                                         for(s16 x=-1; x<=1; x++)
2511                                         {
2512                                                 
2513                                         }
2514                                 }*/
2515                         }
2516                         /*
2517                                 Place other item (not a block)
2518                         */
2519                         else
2520                         {
2521                                 v3s16 blockpos = getNodeBlockPos(p_over);
2522                                 
2523                                 /*
2524                                         Check that the block is loaded so that the item
2525                                         can properly be added to the static list too
2526                                 */
2527                                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2528                                 if(block==NULL)
2529                                 {
2530                                         derr_server<<"Error while placing object: "
2531                                                         "block not found"<<std::endl;
2532                                         return;
2533                                 }
2534
2535                                 dout_server<<"Placing a miscellaneous item on map"
2536                                                 <<std::endl;
2537                                 
2538                                 // Calculate a position for it
2539                                 v3f pos = intToFloat(p_over, BS);
2540                                 //pos.Y -= BS*0.45;
2541                                 pos.Y -= BS*0.25; // let it drop a bit
2542                                 // Randomize a bit
2543                                 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2544                                 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2545
2546                                 /*
2547                                         Create the object
2548                                 */
2549                                 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2550
2551                                 if(obj == NULL)
2552                                 {
2553                                         derr_server<<"WARNING: item resulted in NULL object, "
2554                                                         <<"not placing onto map"
2555                                                         <<std::endl;
2556                                 }
2557                                 else
2558                                 {
2559                                         // Add the object to the environment
2560                                         m_env.addActiveObject(obj);
2561                                         
2562                                         dout_server<<"Placed object"<<std::endl;
2563
2564                                         if(g_settings.getBool("creative_mode") == false)
2565                                         {
2566                                                 // Delete the right amount of items from the slot
2567                                                 u16 dropcount = item->getDropCount();
2568                                                 
2569                                                 // Delete item if all gone
2570                                                 if(item->getCount() <= dropcount)
2571                                                 {
2572                                                         if(item->getCount() < dropcount)
2573                                                                 dstream<<"WARNING: Server: dropped more items"
2574                                                                                 <<" than the slot contains"<<std::endl;
2575                                                         
2576                                                         InventoryList *ilist = player->inventory.getList("main");
2577                                                         if(ilist)
2578                                                                 // Remove from inventory and send inventory
2579                                                                 ilist->deleteItem(item_i);
2580                                                 }
2581                                                 // Else decrement it
2582                                                 else
2583                                                         item->remove(dropcount);
2584                                                 
2585                                                 // Send inventory
2586                                                 UpdateCrafting(peer_id);
2587                                                 SendInventory(peer_id);
2588                                         }
2589                                 }
2590                         }
2591
2592                 } // action == 1
2593
2594                 /*
2595                         Catch invalid actions
2596                 */
2597                 else
2598                 {
2599                         derr_server<<"WARNING: Server: Invalid action "
2600                                         <<action<<std::endl;
2601                 }
2602         }
2603 #if 0
2604         else if(command == TOSERVER_RELEASE)
2605         {
2606                 if(datasize < 3)
2607                         return;
2608                 /*
2609                         length: 3
2610                         [0] u16 command
2611                         [2] u8 button
2612                 */
2613                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2614         }
2615 #endif
2616         else if(command == TOSERVER_SIGNTEXT)
2617         {
2618                 /*
2619                         u16 command
2620                         v3s16 blockpos
2621                         s16 id
2622                         u16 textlen
2623                         textdata
2624                 */
2625                 std::string datastring((char*)&data[2], datasize-2);
2626                 std::istringstream is(datastring, std::ios_base::binary);
2627                 u8 buf[6];
2628                 // Read stuff
2629                 is.read((char*)buf, 6);
2630                 v3s16 blockpos = readV3S16(buf);
2631                 is.read((char*)buf, 2);
2632                 s16 id = readS16(buf);
2633                 is.read((char*)buf, 2);
2634                 u16 textlen = readU16(buf);
2635                 std::string text;
2636                 for(u16 i=0; i<textlen; i++)
2637                 {
2638                         is.read((char*)buf, 1);
2639                         text += (char)buf[0];
2640                 }
2641
2642                 MapBlock *block = NULL;
2643                 try
2644                 {
2645                         block = m_env.getMap().getBlockNoCreate(blockpos);
2646                 }
2647                 catch(InvalidPositionException &e)
2648                 {
2649                         derr_server<<"Error while setting sign text: "
2650                                         "block not found"<<std::endl;
2651                         return;
2652                 }
2653
2654                 MapBlockObject *obj = block->getObject(id);
2655                 if(obj == NULL)
2656                 {
2657                         derr_server<<"Error while setting sign text: "
2658                                         "object not found"<<std::endl;
2659                         return;
2660                 }
2661                 
2662                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2663                 {
2664                         derr_server<<"Error while setting sign text: "
2665                                         "object is not a sign"<<std::endl;
2666                         return;
2667                 }
2668
2669                 ((SignObject*)obj)->setText(text);
2670
2671                 obj->getBlock()->setChangedFlag();
2672         }
2673         else if(command == TOSERVER_SIGNNODETEXT)
2674         {
2675                 /*
2676                         u16 command
2677                         v3s16 p
2678                         u16 textlen
2679                         textdata
2680                 */
2681                 std::string datastring((char*)&data[2], datasize-2);
2682                 std::istringstream is(datastring, std::ios_base::binary);
2683                 u8 buf[6];
2684                 // Read stuff
2685                 is.read((char*)buf, 6);
2686                 v3s16 p = readV3S16(buf);
2687                 is.read((char*)buf, 2);
2688                 u16 textlen = readU16(buf);
2689                 std::string text;
2690                 for(u16 i=0; i<textlen; i++)
2691                 {
2692                         is.read((char*)buf, 1);
2693                         text += (char)buf[0];
2694                 }
2695
2696                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2697                 if(!meta)
2698                         return;
2699                 if(meta->typeId() != CONTENT_SIGN_WALL)
2700                         return;
2701                 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2702                 signmeta->setText(text);
2703                 
2704                 v3s16 blockpos = getNodeBlockPos(p);
2705                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2706                 if(block)
2707                 {
2708                         block->setChangedFlag();
2709                 }
2710
2711                 for(core::map<u16, RemoteClient*>::Iterator
2712                         i = m_clients.getIterator();
2713                         i.atEnd()==false; i++)
2714                 {
2715                         RemoteClient *client = i.getNode()->getValue();
2716                         client->SetBlockNotSent(blockpos);
2717                 }
2718         }
2719         else if(command == TOSERVER_INVENTORY_ACTION)
2720         {
2721                 /*// Ignore inventory changes if in creative mode
2722                 if(g_settings.getBool("creative_mode") == true)
2723                 {
2724                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2725                                         <<std::endl;
2726                         return;
2727                 }*/
2728                 // Strip command and create a stream
2729                 std::string datastring((char*)&data[2], datasize-2);
2730                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2731                 std::istringstream is(datastring, std::ios_base::binary);
2732                 // Create an action
2733                 InventoryAction *a = InventoryAction::deSerialize(is);
2734                 if(a != NULL)
2735                 {
2736                         // Create context
2737                         InventoryContext c;
2738                         c.current_player = player;
2739
2740                         /*
2741                                 Handle craftresult specially if not in creative mode
2742                         */
2743                         bool disable_action = false;
2744                         if(a->getType() == IACTION_MOVE
2745                                         && g_settings.getBool("creative_mode") == false)
2746                         {
2747                                 IMoveAction *ma = (IMoveAction*)a;
2748                                 if(ma->to_inv == "current_player" &&
2749                                                 ma->from_inv == "current_player")
2750                                 {
2751                                         InventoryList *rlist = player->inventory.getList("craftresult");
2752                                         assert(rlist);
2753                                         InventoryList *clist = player->inventory.getList("craft");
2754                                         assert(clist);
2755                                         InventoryList *mlist = player->inventory.getList("main");
2756                                         assert(mlist);
2757                                         /*
2758                                                 Craftresult is no longer preview if something
2759                                                 is moved into it
2760                                         */
2761                                         if(ma->to_list == "craftresult"
2762                                                         && ma->from_list != "craftresult")
2763                                         {
2764                                                 // If it currently is a preview, remove
2765                                                 // its contents
2766                                                 if(player->craftresult_is_preview)
2767                                                 {
2768                                                         rlist->deleteItem(0);
2769                                                 }
2770                                                 player->craftresult_is_preview = false;
2771                                         }
2772                                         /*
2773                                                 Crafting takes place if this condition is true.
2774                                         */
2775                                         if(player->craftresult_is_preview &&
2776                                                         ma->from_list == "craftresult")
2777                                         {
2778                                                 player->craftresult_is_preview = false;
2779                                                 clist->decrementMaterials(1);
2780                                         }
2781                                         /*
2782                                                 If the craftresult is placed on itself, move it to
2783                                                 main inventory instead of doing the action
2784                                         */
2785                                         if(ma->to_list == "craftresult"
2786                                                         && ma->from_list == "craftresult")
2787                                         {
2788                                                 disable_action = true;
2789                                                 
2790                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
2791                                                 mlist->addItem(item1);
2792                                         }
2793                                 }
2794                         }
2795                         
2796                         if(disable_action == false)
2797                         {
2798                                 // Feed action to player inventory
2799                                 a->apply(&c, this);
2800                                 // Eat the action
2801                                 delete a;
2802                         }
2803                         else
2804                         {
2805                                 // Send inventory
2806                                 UpdateCrafting(player->peer_id);
2807                                 SendInventory(player->peer_id);
2808                         }
2809                 }
2810                 else
2811                 {
2812                         dstream<<"TOSERVER_INVENTORY_ACTION: "
2813                                         <<"InventoryAction::deSerialize() returned NULL"
2814                                         <<std::endl;
2815                 }
2816         }
2817         else if(command == TOSERVER_CHAT_MESSAGE)
2818         {
2819                 /*
2820                         u16 command
2821                         u16 length
2822                         wstring message
2823                 */
2824                 u8 buf[6];
2825                 std::string datastring((char*)&data[2], datasize-2);
2826                 std::istringstream is(datastring, std::ios_base::binary);
2827                 
2828                 // Read stuff
2829                 is.read((char*)buf, 2);
2830                 u16 len = readU16(buf);
2831                 
2832                 std::wstring message;
2833                 for(u16 i=0; i<len; i++)
2834                 {
2835                         is.read((char*)buf, 2);
2836                         message += (wchar_t)readU16(buf);
2837                 }
2838
2839                 // Get player name of this client
2840                 std::wstring name = narrow_to_wide(player->getName());
2841                 
2842                 // Line to send to players
2843                 std::wstring line;
2844                 // Whether to send to the player that sent the line
2845                 bool send_to_sender = false;
2846                 // Whether to send to other players
2847                 bool send_to_others = false;
2848                 
2849                 // Parse commands
2850                 std::wstring commandprefix = L"/#";
2851                 if(message.substr(0, commandprefix.size()) == commandprefix)
2852                 {
2853                         line += L"Server: ";
2854
2855                         message = message.substr(commandprefix.size());
2856                         // Get player name as narrow string
2857                         std::string name_s = player->getName();
2858                         // Convert message to narrow string
2859                         std::string message_s = wide_to_narrow(message);
2860                         // Operator is the single name defined in config.
2861                         std::string operator_name = g_settings.get("name");
2862                         bool is_operator = (operator_name != "" &&
2863                                         wide_to_narrow(name) == operator_name);
2864                         bool valid_command = false;
2865                         if(message_s == "help")
2866                         {
2867                                 line += L"-!- Available commands: ";
2868                                 line += L"status ";
2869                                 if(is_operator)
2870                                 {
2871                                         line += L"shutdown setting time ";
2872                                 }
2873                                 else
2874                                 {
2875                                 }
2876                                 send_to_sender = true;
2877                                 valid_command = true;
2878                         }
2879                         else if(message_s == "status")
2880                         {
2881                                 line = getStatusString();
2882                                 send_to_sender = true;
2883                                 valid_command = true;
2884                         }
2885                         else if(is_operator)
2886                         {
2887                                 if(message_s == "shutdown")
2888                                 {
2889                                         dstream<<DTIME<<" Server: Operator requested shutdown."
2890                                                         <<std::endl;
2891                                         m_shutdown_requested.set(true);
2892                                         
2893                                         line += L"*** Server shutting down (operator request)";
2894                                         send_to_sender = true;
2895                                         valid_command = true;
2896                                 }
2897                                 else if(message_s.substr(0,8) == "setting ")
2898                                 {
2899                                         std::string confline = message_s.substr(8);
2900                                         g_settings.parseConfigLine(confline);
2901                                         line += L"-!- Setting changed.";
2902                                         send_to_sender = true;
2903                                         valid_command = true;
2904                                 }
2905                                 else if(message_s.substr(0,5) == "time ")
2906                                 {
2907                                         u32 time = stoi(message_s.substr(5));
2908                                         m_time_of_day.set(time);
2909                                         m_time_of_day_send_timer = 0;
2910                                         line += L"-!- time_of_day changed.";
2911                                         send_to_sender = true;
2912                                         valid_command = true;
2913                                 }
2914                         }
2915                         
2916                         if(valid_command == false)
2917                         {
2918                                 line += L"-!- Invalid command: " + message;
2919                                 send_to_sender = true;
2920                         }
2921                 }
2922                 else
2923                 {
2924                         line += L"<";
2925                         /*if(is_operator)
2926                                 line += L"@";*/
2927                         line += name;
2928                         line += L"> ";
2929                         line += message;
2930                         send_to_others = true;
2931                 }
2932                 
2933                 if(line != L"")
2934                 {
2935                         dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2936
2937                         /*
2938                                 Send the message to clients
2939                         */
2940                         for(core::map<u16, RemoteClient*>::Iterator
2941                                 i = m_clients.getIterator();
2942                                 i.atEnd() == false; i++)
2943                         {
2944                                 // Get client and check that it is valid
2945                                 RemoteClient *client = i.getNode()->getValue();
2946                                 assert(client->peer_id == i.getNode()->getKey());
2947                                 if(client->serialization_version == SER_FMT_VER_INVALID)
2948                                         continue;
2949
2950                                 // Filter recipient
2951                                 bool sender_selected = (peer_id == client->peer_id);
2952                                 if(sender_selected == true && send_to_sender == false)
2953                                         continue;
2954                                 if(sender_selected == false && send_to_others == false)
2955                                         continue;
2956
2957                                 SendChatMessage(client->peer_id, line);
2958                         }
2959                 }
2960         }
2961         else if(command == TOSERVER_DAMAGE)
2962         {
2963                 if(g_settings.getBool("enable_damage"))
2964                 {
2965                         std::string datastring((char*)&data[2], datasize-2);
2966                         std::istringstream is(datastring, std::ios_base::binary);
2967                         u8 damage = readU8(is);
2968                         if(player->hp > damage)
2969                         {
2970                                 player->hp -= damage;
2971                         }
2972                         else
2973                         {
2974                                 player->hp = 0;
2975
2976                                 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2977                                                 <<std::endl;
2978                                 
2979                                 v3f pos = findSpawnPos(m_env.getServerMap());
2980                                 player->setPosition(pos);
2981                                 player->hp = 20;
2982                                 SendMovePlayer(player);
2983                                 SendPlayerHP(player);
2984                                 
2985                                 //TODO: Throw items around
2986                         }
2987                 }
2988
2989                 SendPlayerHP(player);
2990         }
2991         else
2992         {
2993                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2994                                 "unknown command "<<command<<std::endl;
2995         }
2996         
2997         } //try
2998         catch(SendFailedException &e)
2999         {
3000                 derr_server<<"Server::ProcessData(): SendFailedException: "
3001                                 <<"what="<<e.what()
3002                                 <<std::endl;
3003         }
3004 }
3005
3006 void Server::onMapEditEvent(MapEditEvent *event)
3007 {
3008         dstream<<"Server::onMapEditEvent()"<<std::endl;
3009         if(m_ignore_map_edit_events)
3010                 return;
3011         MapEditEvent *e = event->clone();
3012         m_unsent_map_edit_queue.push_back(e);
3013 }
3014
3015 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3016 {
3017         if(id == "current_player")
3018         {
3019                 assert(c->current_player);
3020                 return &(c->current_player->inventory);
3021         }
3022         
3023         Strfnd fn(id);
3024         std::string id0 = fn.next(":");
3025
3026         if(id0 == "nodemeta")
3027         {
3028                 v3s16 p;
3029                 p.X = stoi(fn.next(","));
3030                 p.Y = stoi(fn.next(","));
3031                 p.Z = stoi(fn.next(","));
3032                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3033                 if(meta)
3034                         return meta->getInventory();
3035                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3036                                 <<"no metadata found"<<std::endl;
3037                 return NULL;
3038         }
3039
3040         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3041         return NULL;
3042 }
3043 void Server::inventoryModified(InventoryContext *c, std::string id)
3044 {
3045         if(id == "current_player")
3046         {
3047                 assert(c->current_player);
3048                 // Send inventory
3049                 UpdateCrafting(c->current_player->peer_id);
3050                 SendInventory(c->current_player->peer_id);
3051                 return;
3052         }
3053         
3054         Strfnd fn(id);
3055         std::string id0 = fn.next(":");
3056
3057         if(id0 == "nodemeta")
3058         {
3059                 v3s16 p;
3060                 p.X = stoi(fn.next(","));
3061                 p.Y = stoi(fn.next(","));
3062                 p.Z = stoi(fn.next(","));
3063                 v3s16 blockpos = getNodeBlockPos(p);
3064
3065                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3066                 if(meta)
3067                         meta->inventoryModified();
3068
3069                 for(core::map<u16, RemoteClient*>::Iterator
3070                         i = m_clients.getIterator();
3071                         i.atEnd()==false; i++)
3072                 {
3073                         RemoteClient *client = i.getNode()->getValue();
3074                         client->SetBlockNotSent(blockpos);
3075                 }
3076
3077                 return;
3078         }
3079
3080         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3081 }
3082
3083 core::list<PlayerInfo> Server::getPlayerInfo()
3084 {
3085         DSTACK(__FUNCTION_NAME);
3086         JMutexAutoLock envlock(m_env_mutex);
3087         JMutexAutoLock conlock(m_con_mutex);
3088         
3089         core::list<PlayerInfo> list;
3090
3091         core::list<Player*> players = m_env.getPlayers();
3092         
3093         core::list<Player*>::Iterator i;
3094         for(i = players.begin();
3095                         i != players.end(); i++)
3096         {
3097                 PlayerInfo info;
3098
3099                 Player *player = *i;
3100
3101                 try{
3102                         con::Peer *peer = m_con.GetPeer(player->peer_id);
3103                         // Copy info from peer to info struct
3104                         info.id = peer->id;
3105                         info.address = peer->address;
3106                         info.avg_rtt = peer->avg_rtt;
3107                 }
3108                 catch(con::PeerNotFoundException &e)
3109                 {
3110                         // Set dummy peer info
3111                         info.id = 0;
3112                         info.address = Address(0,0,0,0,0);
3113                         info.avg_rtt = 0.0;
3114                 }
3115
3116                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3117                 info.position = player->getPosition();
3118
3119                 list.push_back(info);
3120         }
3121
3122         return list;
3123 }
3124
3125
3126 void Server::peerAdded(con::Peer *peer)
3127 {
3128         DSTACK(__FUNCTION_NAME);
3129         dout_server<<"Server::peerAdded(): peer->id="
3130                         <<peer->id<<std::endl;
3131         
3132         PeerChange c;
3133         c.type = PEER_ADDED;
3134         c.peer_id = peer->id;
3135         c.timeout = false;
3136         m_peer_change_queue.push_back(c);
3137 }
3138
3139 void Server::deletingPeer(con::Peer *peer, bool timeout)
3140 {
3141         DSTACK(__FUNCTION_NAME);
3142         dout_server<<"Server::deletingPeer(): peer->id="
3143                         <<peer->id<<", timeout="<<timeout<<std::endl;
3144         
3145         PeerChange c;
3146         c.type = PEER_REMOVED;
3147         c.peer_id = peer->id;
3148         c.timeout = timeout;
3149         m_peer_change_queue.push_back(c);
3150 }
3151
3152 /*
3153         Static send methods
3154 */
3155
3156 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3157 {
3158         DSTACK(__FUNCTION_NAME);
3159         std::ostringstream os(std::ios_base::binary);
3160
3161         writeU16(os, TOCLIENT_HP);
3162         writeU8(os, hp);
3163
3164         // Make data buffer
3165         std::string s = os.str();
3166         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3167         // Send as reliable
3168         con.Send(peer_id, 0, data, true);
3169 }
3170
3171 /*
3172         Non-static send methods
3173 */
3174
3175 void Server::SendObjectData(float dtime)
3176 {
3177         DSTACK(__FUNCTION_NAME);
3178
3179         core::map<v3s16, bool> stepped_blocks;
3180         
3181         for(core::map<u16, RemoteClient*>::Iterator
3182                 i = m_clients.getIterator();
3183                 i.atEnd() == false; i++)
3184         {
3185                 u16 peer_id = i.getNode()->getKey();
3186                 RemoteClient *client = i.getNode()->getValue();
3187                 assert(client->peer_id == peer_id);
3188                 
3189                 if(client->serialization_version == SER_FMT_VER_INVALID)
3190                         continue;
3191                 
3192                 client->SendObjectData(this, dtime, stepped_blocks);
3193         }
3194 }
3195
3196 void Server::SendPlayerInfos()
3197 {
3198         DSTACK(__FUNCTION_NAME);
3199
3200         //JMutexAutoLock envlock(m_env_mutex);
3201         
3202         // Get connected players
3203         core::list<Player*> players = m_env.getPlayers(true);
3204         
3205         u32 player_count = players.getSize();
3206         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3207
3208         SharedBuffer<u8> data(datasize);
3209         writeU16(&data[0], TOCLIENT_PLAYERINFO);
3210         
3211         u32 start = 2;
3212         core::list<Player*>::Iterator i;
3213         for(i = players.begin();
3214                         i != players.end(); i++)
3215         {
3216                 Player *player = *i;
3217
3218                 /*dstream<<"Server sending player info for player with "
3219                                 "peer_id="<<player->peer_id<<std::endl;*/
3220                 
3221                 writeU16(&data[start], player->peer_id);
3222                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3223                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3224                 start += 2+PLAYERNAME_SIZE;
3225         }
3226
3227         //JMutexAutoLock conlock(m_con_mutex);
3228
3229         // Send as reliable
3230         m_con.SendToAll(0, data, true);
3231 }
3232
3233 void Server::SendInventory(u16 peer_id)
3234 {
3235         DSTACK(__FUNCTION_NAME);
3236         
3237         Player* player = m_env.getPlayer(peer_id);
3238         assert(player);
3239
3240         /*
3241                 Serialize it
3242         */
3243
3244         std::ostringstream os;
3245         //os.imbue(std::locale("C"));
3246
3247         player->inventory.serialize(os);
3248
3249         std::string s = os.str();
3250         
3251         SharedBuffer<u8> data(s.size()+2);
3252         writeU16(&data[0], TOCLIENT_INVENTORY);
3253         memcpy(&data[2], s.c_str(), s.size());
3254         
3255         // Send as reliable
3256         m_con.Send(peer_id, 0, data, true);
3257 }
3258
3259 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3260 {
3261         DSTACK(__FUNCTION_NAME);
3262         
3263         std::ostringstream os(std::ios_base::binary);
3264         u8 buf[12];
3265         
3266         // Write command
3267         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3268         os.write((char*)buf, 2);
3269         
3270         // Write length
3271         writeU16(buf, message.size());
3272         os.write((char*)buf, 2);
3273         
3274         // Write string
3275         for(u32 i=0; i<message.size(); i++)
3276         {
3277                 u16 w = message[i];
3278                 writeU16(buf, w);
3279                 os.write((char*)buf, 2);
3280         }
3281         
3282         // Make data buffer
3283         std::string s = os.str();
3284         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3285         // Send as reliable
3286         m_con.Send(peer_id, 0, data, true);
3287 }
3288
3289 void Server::BroadcastChatMessage(const std::wstring &message)
3290 {
3291         for(core::map<u16, RemoteClient*>::Iterator
3292                 i = m_clients.getIterator();
3293                 i.atEnd() == false; i++)
3294         {
3295                 // Get client and check that it is valid
3296                 RemoteClient *client = i.getNode()->getValue();
3297                 assert(client->peer_id == i.getNode()->getKey());
3298                 if(client->serialization_version == SER_FMT_VER_INVALID)
3299                         continue;
3300
3301                 SendChatMessage(client->peer_id, message);
3302         }
3303 }
3304
3305 void Server::SendPlayerHP(Player *player)
3306 {
3307         SendHP(m_con, player->peer_id, player->hp);
3308 }
3309
3310 void Server::SendMovePlayer(Player *player)
3311 {
3312         DSTACK(__FUNCTION_NAME);
3313         std::ostringstream os(std::ios_base::binary);
3314
3315         writeU16(os, TOCLIENT_MOVE_PLAYER);
3316         writeV3F1000(os, player->getPosition());
3317         writeF1000(os, player->getPitch());
3318         writeF1000(os, player->getYaw());
3319         
3320         {
3321                 v3f pos = player->getPosition();
3322                 f32 pitch = player->getPitch();
3323                 f32 yaw = player->getYaw();
3324                 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3325                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3326                                 <<" pitch="<<pitch
3327                                 <<" yaw="<<yaw
3328                                 <<std::endl;
3329         }
3330
3331         // Make data buffer
3332         std::string s = os.str();
3333         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3334         // Send as reliable
3335         m_con.Send(player->peer_id, 0, data, true);
3336 }
3337
3338 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3339         core::list<u16> *far_players, float far_d_nodes)
3340 {
3341         float maxd = far_d_nodes*BS;
3342         v3f p_f = intToFloat(p, BS);
3343
3344         // Create packet
3345         u32 replysize = 8;
3346         SharedBuffer<u8> reply(replysize);
3347         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3348         writeS16(&reply[2], p.X);
3349         writeS16(&reply[4], p.Y);
3350         writeS16(&reply[6], p.Z);
3351
3352         for(core::map<u16, RemoteClient*>::Iterator
3353                 i = m_clients.getIterator();
3354                 i.atEnd() == false; i++)
3355         {
3356                 // Get client and check that it is valid
3357                 RemoteClient *client = i.getNode()->getValue();
3358                 assert(client->peer_id == i.getNode()->getKey());
3359                 if(client->serialization_version == SER_FMT_VER_INVALID)
3360                         continue;
3361
3362                 // Don't send if it's the same one
3363                 if(client->peer_id == ignore_id)
3364                         continue;
3365                 
3366                 if(far_players)
3367                 {
3368                         // Get player
3369                         Player *player = m_env.getPlayer(client->peer_id);
3370                         if(player)
3371                         {
3372                                 // If player is far away, only set modified blocks not sent
3373                                 v3f player_pos = player->getPosition();
3374                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3375                                 {
3376                                         far_players->push_back(client->peer_id);
3377                                         continue;
3378                                 }
3379                         }
3380                 }
3381
3382                 // Send as reliable
3383                 m_con.Send(client->peer_id, 0, reply, true);
3384         }
3385 }
3386
3387 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3388                 core::list<u16> *far_players, float far_d_nodes)
3389 {
3390         float maxd = far_d_nodes*BS;
3391         v3f p_f = intToFloat(p, BS);
3392
3393         for(core::map<u16, RemoteClient*>::Iterator
3394                 i = m_clients.getIterator();
3395                 i.atEnd() == false; i++)
3396         {
3397                 // Get client and check that it is valid
3398                 RemoteClient *client = i.getNode()->getValue();
3399                 assert(client->peer_id == i.getNode()->getKey());
3400                 if(client->serialization_version == SER_FMT_VER_INVALID)
3401                         continue;
3402
3403                 // Don't send if it's the same one
3404                 if(client->peer_id == ignore_id)
3405                         continue;
3406
3407                 if(far_players)
3408                 {
3409                         // Get player
3410                         Player *player = m_env.getPlayer(client->peer_id);
3411                         if(player)
3412                         {
3413                                 // If player is far away, only set modified blocks not sent
3414                                 v3f player_pos = player->getPosition();
3415                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3416                                 {
3417                                         far_players->push_back(client->peer_id);
3418                                         continue;
3419                                 }
3420                         }
3421                 }
3422
3423                 // Create packet
3424                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3425                 SharedBuffer<u8> reply(replysize);
3426                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3427                 writeS16(&reply[2], p.X);
3428                 writeS16(&reply[4], p.Y);
3429                 writeS16(&reply[6], p.Z);
3430                 n.serialize(&reply[8], client->serialization_version);
3431
3432                 // Send as reliable
3433                 m_con.Send(client->peer_id, 0, reply, true);
3434         }
3435 }
3436
3437 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3438 {
3439         DSTACK(__FUNCTION_NAME);
3440
3441         v3s16 p = block->getPos();
3442         
3443 #if 0
3444         // Analyze it a bit
3445         bool completely_air = true;
3446         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3447         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3448         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3449         {
3450                 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3451                 {
3452                         completely_air = false;
3453                         x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3454                 }
3455         }
3456
3457         // Print result
3458         dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3459         if(completely_air)
3460                 dstream<<"[completely air] ";
3461         dstream<<std::endl;
3462 #endif
3463
3464         /*
3465                 Create a packet with the block in the right format
3466         */
3467         
3468         std::ostringstream os(std::ios_base::binary);
3469         block->serialize(os, ver);
3470         std::string s = os.str();
3471         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3472
3473         u32 replysize = 8 + blockdata.getSize();
3474         SharedBuffer<u8> reply(replysize);
3475         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3476         writeS16(&reply[2], p.X);
3477         writeS16(&reply[4], p.Y);
3478         writeS16(&reply[6], p.Z);
3479         memcpy(&reply[8], *blockdata, blockdata.getSize());
3480
3481         /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3482                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3483         
3484         /*
3485                 Send packet
3486         */
3487         m_con.Send(peer_id, 1, reply, true);
3488 }
3489
3490 void Server::SendBlocks(float dtime)
3491 {
3492         DSTACK(__FUNCTION_NAME);
3493
3494         JMutexAutoLock envlock(m_env_mutex);
3495         JMutexAutoLock conlock(m_con_mutex);
3496
3497         //TimeTaker timer("Server::SendBlocks");
3498
3499         core::array<PrioritySortedBlockTransfer> queue;
3500
3501         s32 total_sending = 0;
3502
3503         for(core::map<u16, RemoteClient*>::Iterator
3504                 i = m_clients.getIterator();
3505                 i.atEnd() == false; i++)
3506         {
3507                 RemoteClient *client = i.getNode()->getValue();
3508                 assert(client->peer_id == i.getNode()->getKey());
3509
3510                 total_sending += client->SendingCount();
3511                 
3512                 if(client->serialization_version == SER_FMT_VER_INVALID)
3513                         continue;
3514                 
3515                 client->GetNextBlocks(this, dtime, queue);
3516         }
3517
3518         // Sort.
3519         // Lowest priority number comes first.
3520         // Lowest is most important.
3521         queue.sort();
3522
3523         for(u32 i=0; i<queue.size(); i++)
3524         {
3525                 //TODO: Calculate limit dynamically
3526                 if(total_sending >= g_settings.getS32
3527                                 ("max_simultaneous_block_sends_server_total"))
3528                         break;
3529                 
3530                 PrioritySortedBlockTransfer q = queue[i];
3531
3532                 MapBlock *block = NULL;
3533                 try
3534                 {
3535                         block = m_env.getMap().getBlockNoCreate(q.pos);
3536                 }
3537                 catch(InvalidPositionException &e)
3538                 {
3539                         continue;
3540                 }
3541
3542                 RemoteClient *client = getClient(q.peer_id);
3543
3544                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3545
3546                 client->SentBlock(q.pos);
3547
3548                 total_sending++;
3549         }
3550 }
3551
3552 /*
3553         Something random
3554 */
3555
3556 void Server::UpdateCrafting(u16 peer_id)
3557 {
3558         DSTACK(__FUNCTION_NAME);
3559         
3560         Player* player = m_env.getPlayer(peer_id);
3561         assert(player);
3562
3563         /*
3564                 Calculate crafting stuff
3565         */
3566         if(g_settings.getBool("creative_mode") == false)
3567         {
3568                 InventoryList *clist = player->inventory.getList("craft");
3569                 InventoryList *rlist = player->inventory.getList("craftresult");
3570
3571                 if(rlist->getUsedSlots() == 0)
3572                         player->craftresult_is_preview = true;
3573
3574                 if(rlist && player->craftresult_is_preview)
3575                 {
3576                         rlist->clearItems();
3577                 }
3578                 if(clist && rlist && player->craftresult_is_preview)
3579                 {
3580                         InventoryItem *items[9];
3581                         for(u16 i=0; i<9; i++)
3582                         {
3583                                 items[i] = clist->getItem(i);
3584                         }
3585                         
3586                         bool found = false;
3587
3588                         // Wood
3589                         if(!found)
3590                         {
3591                                 ItemSpec specs[9];
3592                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3593                                 if(checkItemCombination(items, specs))
3594                                 {
3595                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3596                                         found = true;
3597                                 }
3598                         }
3599
3600                         // Stick
3601                         if(!found)
3602                         {
3603                                 ItemSpec specs[9];
3604                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3605                                 if(checkItemCombination(items, specs))
3606                                 {
3607                                         rlist->addItem(new CraftItem("Stick", 4));
3608                                         found = true;
3609                                 }
3610                         }
3611
3612                         // Sign
3613                         if(!found)
3614                         {
3615                                 ItemSpec specs[9];
3616                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3617                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3618                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3619                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3620                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3621                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3622                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3623                                 if(checkItemCombination(items, specs))
3624                                 {
3625                                         //rlist->addItem(new MapBlockObjectItem("Sign"));
3626                                         rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3627                                         found = true;
3628                                 }
3629                         }
3630
3631                         // Torch
3632                         if(!found)
3633                         {
3634                                 ItemSpec specs[9];
3635                                 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3636                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3637                                 if(checkItemCombination(items, specs))
3638                                 {
3639                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3640                                         found = true;
3641                                 }
3642                         }
3643
3644                         // Wooden pick
3645                         if(!found)
3646                         {
3647                                 ItemSpec specs[9];
3648                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3649                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3650                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3651                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3652                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3653                                 if(checkItemCombination(items, specs))
3654                                 {
3655                                         rlist->addItem(new ToolItem("WPick", 0));
3656                                         found = true;
3657                                 }
3658                         }
3659
3660                         // Stone pick
3661                         if(!found)
3662                         {
3663                                 ItemSpec specs[9];
3664                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3665                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3666                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3667                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3668                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3669                                 if(checkItemCombination(items, specs))
3670                                 {
3671                                         rlist->addItem(new ToolItem("STPick", 0));
3672                                         found = true;
3673                                 }
3674                         }
3675
3676                         // Steel pick
3677                         if(!found)
3678                         {
3679                                 ItemSpec specs[9];
3680                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3681                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3682                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3683                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3684                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3685                                 if(checkItemCombination(items, specs))
3686                                 {
3687                                         rlist->addItem(new ToolItem("SteelPick", 0));
3688                                         found = true;
3689                                 }
3690                         }
3691
3692                         // Mese pick
3693                         if(!found)
3694                         {
3695                                 ItemSpec specs[9];
3696                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3697                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3698                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3699                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3700                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3701                                 if(checkItemCombination(items, specs))
3702                                 {
3703                                         rlist->addItem(new ToolItem("MesePick", 0));
3704                                         found = true;
3705                                 }
3706                         }
3707
3708                         // Wooden shovel
3709                         if(!found)
3710                         {
3711                                 ItemSpec specs[9];
3712                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3713                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3714                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3715                                 if(checkItemCombination(items, specs))
3716                                 {
3717                                         rlist->addItem(new ToolItem("WShovel", 0));
3718                                         found = true;
3719                                 }
3720                         }
3721
3722                         // Stone shovel
3723                         if(!found)
3724                         {
3725                                 ItemSpec specs[9];
3726                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3727                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3728                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3729                                 if(checkItemCombination(items, specs))
3730                                 {
3731                                         rlist->addItem(new ToolItem("STShovel", 0));
3732                                         found = true;
3733                                 }
3734                         }
3735
3736                         // Steel shovel
3737                         if(!found)
3738                         {
3739                                 ItemSpec specs[9];
3740                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3741                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3742                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3743                                 if(checkItemCombination(items, specs))
3744                                 {
3745                                         rlist->addItem(new ToolItem("SteelShovel", 0));
3746                                         found = true;
3747                                 }
3748                         }
3749
3750                         // Wooden axe
3751                         if(!found)
3752                         {
3753                                 ItemSpec specs[9];
3754                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3755                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3756                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3757                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3758                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3759                                 if(checkItemCombination(items, specs))
3760                                 {
3761                                         rlist->addItem(new ToolItem("WAxe", 0));
3762                                         found = true;
3763                                 }
3764                         }
3765
3766                         // Stone axe
3767                         if(!found)
3768                         {
3769                                 ItemSpec specs[9];
3770                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3771                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3772                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3773                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3774                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3775                                 if(checkItemCombination(items, specs))
3776                                 {
3777                                         rlist->addItem(new ToolItem("STAxe", 0));
3778                                         found = true;
3779                                 }
3780                         }
3781
3782                         // Steel axe
3783                         if(!found)
3784                         {
3785                                 ItemSpec specs[9];
3786                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3787                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3788                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3789                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3790                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3791                                 if(checkItemCombination(items, specs))
3792                                 {
3793                                         rlist->addItem(new ToolItem("SteelAxe", 0));
3794                                         found = true;
3795                                 }
3796                         }
3797
3798                         // Wooden sword
3799                         if(!found)
3800                         {
3801                                 ItemSpec specs[9];
3802                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3803                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3804                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3805                                 if(checkItemCombination(items, specs))
3806                                 {
3807                                         rlist->addItem(new ToolItem("WSword", 0));
3808                                         found = true;
3809                                 }
3810                         }
3811
3812                         // Stone sword
3813                         if(!found)
3814                         {
3815                                 ItemSpec specs[9];
3816                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3817                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3818                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3819                                 if(checkItemCombination(items, specs))
3820                                 {
3821                                         rlist->addItem(new ToolItem("STSword", 0));
3822                                         found = true;
3823                                 }
3824                         }
3825
3826                         // Steel sword
3827                         if(!found)
3828                         {
3829                                 ItemSpec specs[9];
3830                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3831                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3832                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3833                                 if(checkItemCombination(items, specs))
3834                                 {
3835                                         rlist->addItem(new ToolItem("SteelSword", 0));
3836                                         found = true;
3837                                 }
3838                         }
3839
3840                         // Chest
3841                         if(!found)
3842                         {
3843                                 ItemSpec specs[9];
3844                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3845                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3846                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3847                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3848                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3849                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3850                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3851                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3852                                 if(checkItemCombination(items, specs))
3853                                 {
3854                                         rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3855                                         found = true;
3856                                 }
3857                         }
3858
3859                         // Furnace
3860                         if(!found)
3861                         {
3862                                 ItemSpec specs[9];
3863                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3864                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3865                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3866                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3867                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3868                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3869                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3870                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3871                                 if(checkItemCombination(items, specs))
3872                                 {
3873                                         rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3874                                         found = true;
3875                                 }
3876                         }
3877
3878                         // Steel block
3879                         if(!found)
3880                         {
3881                                 ItemSpec specs[9];
3882                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3883                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3884                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3885                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3886                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3887                                 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3888                                 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3889                                 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3890                                 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3891                                 if(checkItemCombination(items, specs))
3892                                 {
3893                                         rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3894                                         found = true;
3895                                 }
3896                         }
3897                 }
3898         
3899         } // if creative_mode == false
3900 }
3901
3902 RemoteClient* Server::getClient(u16 peer_id)
3903 {
3904         DSTACK(__FUNCTION_NAME);
3905         //JMutexAutoLock lock(m_con_mutex);
3906         core::map<u16, RemoteClient*>::Node *n;
3907         n = m_clients.find(peer_id);
3908         // A client should exist for all peers
3909         assert(n != NULL);
3910         return n->getValue();
3911 }
3912
3913 std::wstring Server::getStatusString()
3914 {
3915         std::wostringstream os(std::ios_base::binary);
3916         os<<L"# Server: ";
3917         // Version
3918         os<<L"version="<<narrow_to_wide(VERSION_STRING);
3919         // Uptime
3920         os<<L", uptime="<<m_uptime.get();
3921         // Information about clients
3922         os<<L", clients={";
3923         for(core::map<u16, RemoteClient*>::Iterator
3924                 i = m_clients.getIterator();
3925                 i.atEnd() == false; i++)
3926         {
3927                 // Get client and check that it is valid
3928                 RemoteClient *client = i.getNode()->getValue();
3929                 assert(client->peer_id == i.getNode()->getKey());
3930                 if(client->serialization_version == SER_FMT_VER_INVALID)
3931                         continue;
3932                 // Get player
3933                 Player *player = m_env.getPlayer(client->peer_id);
3934                 // Get name of player
3935                 std::wstring name = L"unknown";
3936                 if(player != NULL)
3937                         name = narrow_to_wide(player->getName());
3938                 // Add name to information string
3939                 os<<name<<L",";
3940         }
3941         os<<L"}";
3942         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3943                 os<<" WARNING: Map saving is disabled."<<std::endl;
3944         return os.str();
3945 }
3946
3947
3948 void setCreativeInventory(Player *player)
3949 {
3950         player->resetInventory();
3951         
3952         // Give some good tools
3953         {
3954                 InventoryItem *item = new ToolItem("MesePick", 0);
3955                 void* r = player->inventory.addItem("main", item);
3956                 assert(r == NULL);
3957         }
3958         {
3959                 InventoryItem *item = new ToolItem("SteelPick", 0);
3960                 void* r = player->inventory.addItem("main", item);
3961                 assert(r == NULL);
3962         }
3963         {
3964                 InventoryItem *item = new ToolItem("SteelAxe", 0);
3965                 void* r = player->inventory.addItem("main", item);
3966                 assert(r == NULL);
3967         }
3968         {
3969                 InventoryItem *item = new ToolItem("SteelShovel", 0);
3970                 void* r = player->inventory.addItem("main", item);
3971                 assert(r == NULL);
3972         }
3973
3974         /*
3975                 Give materials
3976         */
3977         
3978         // CONTENT_IGNORE-terminated list
3979         u8 material_items[] = {
3980                 CONTENT_TORCH,
3981                 CONTENT_COBBLE,
3982                 CONTENT_MUD,
3983                 CONTENT_STONE,
3984                 CONTENT_SAND,
3985                 CONTENT_TREE,
3986                 CONTENT_LEAVES,
3987                 CONTENT_MESE,
3988                 CONTENT_WATERSOURCE,
3989                 CONTENT_CLOUD,
3990                 CONTENT_CHEST,
3991                 CONTENT_FURNACE,
3992                 CONTENT_SIGN_WALL,
3993                 CONTENT_IGNORE
3994         };
3995         
3996         u8 *mip = material_items;
3997         for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3998         {
3999                 if(*mip == CONTENT_IGNORE)
4000                         break;
4001
4002                 InventoryItem *item = new MaterialItem(*mip, 1);
4003                 player->inventory.addItem("main", item);
4004
4005                 mip++;
4006         }
4007
4008 #if 0
4009         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4010         
4011         // add torch first
4012         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4013         player->inventory.addItem("main", item);
4014         
4015         // Then others
4016         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4017         {
4018                 // Skip some materials
4019                 if(i == CONTENT_WATER || i == CONTENT_TORCH
4020                         || i == CONTENT_COALSTONE)
4021                         continue;
4022
4023                 InventoryItem *item = new MaterialItem(i, 1);
4024                 player->inventory.addItem("main", item);
4025         }
4026 #endif
4027
4028         /*// Sign
4029         {
4030                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4031                 void* r = player->inventory.addItem("main", item);
4032                 assert(r == NULL);
4033         }*/
4034 }
4035
4036 v3f findSpawnPos(ServerMap &map)
4037 {
4038         //return v3f(50,50,50)*BS;
4039         
4040         v2s16 nodepos;
4041         s16 groundheight = 0;
4042         
4043         // Try to find a good place a few times
4044         for(s32 i=0; i<1000; i++)
4045         {
4046                 s32 range = 1 + i;
4047                 // We're going to try to throw the player to this position
4048                 nodepos = v2s16(-range + (myrand()%(range*2)),
4049                                 -range + (myrand()%(range*2)));
4050                 v2s16 sectorpos = getNodeSectorPos(nodepos);
4051                 // Get sector (NOTE: Don't get because it's slow)
4052                 //m_env.getMap().emergeSector(sectorpos);
4053                 // Get ground height at point (fallbacks to heightmap function)
4054                 groundheight = map.findGroundLevel(nodepos);
4055                 // Don't go underwater
4056                 if(groundheight < WATER_LEVEL)
4057                 {
4058                         //dstream<<"-> Underwater"<<std::endl;
4059                         continue;
4060                 }
4061                 // Don't go to high places
4062                 if(groundheight > WATER_LEVEL + 4)
4063                 {
4064                         //dstream<<"-> Underwater"<<std::endl;
4065                         continue;
4066                 }
4067
4068                 // Found a good place
4069                 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4070                 break;
4071         }
4072         
4073         // If no suitable place was not found, go above water at least.
4074         if(groundheight < WATER_LEVEL)
4075                 groundheight = WATER_LEVEL;
4076
4077         return intToFloat(v3s16(
4078                         nodepos.X,
4079                         groundheight + 2,
4080                         nodepos.Y
4081                         ), BS);
4082 }
4083
4084 Player *Server::emergePlayer(const char *name, const char *password,
4085                 u16 peer_id)
4086 {
4087         /*
4088                 Try to get an existing player
4089         */
4090         Player *player = m_env.getPlayer(name);
4091         if(player != NULL)
4092         {
4093                 // If player is already connected, cancel
4094                 if(player->peer_id != 0)
4095                 {
4096                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
4097                         return NULL;
4098                 }
4099
4100                 // Got one.
4101                 player->peer_id = peer_id;
4102                 
4103                 // Reset inventory to creative if in creative mode
4104                 if(g_settings.getBool("creative_mode"))
4105                 {
4106                         setCreativeInventory(player);
4107                 }
4108
4109                 return player;
4110         }
4111
4112         /*
4113                 If player with the wanted peer_id already exists, cancel.
4114         */
4115         if(m_env.getPlayer(peer_id) != NULL)
4116         {
4117                 dstream<<"emergePlayer(): Player with wrong name but same"
4118                                 " peer_id already exists"<<std::endl;
4119                 return NULL;
4120         }
4121         
4122         /*
4123                 Create a new player
4124         */
4125         {
4126                 player = new ServerRemotePlayer();
4127                 //player->peer_id = c.peer_id;
4128                 //player->peer_id = PEER_ID_INEXISTENT;
4129                 player->peer_id = peer_id;
4130                 player->updateName(name);
4131
4132                 /*
4133                         Set player position
4134                 */
4135                 
4136                 dstream<<"Server: Finding spawn place for player \""
4137                                 <<player->getName()<<"\""<<std::endl;
4138
4139                 v3f pos = findSpawnPos(m_env.getServerMap());
4140
4141                 player->setPosition(pos);
4142
4143                 /*
4144                         Add player to environment
4145                 */
4146
4147                 m_env.addPlayer(player);
4148
4149                 /*
4150                         Add stuff to inventory
4151                 */
4152                 
4153                 if(g_settings.getBool("creative_mode"))
4154                 {
4155                         setCreativeInventory(player);
4156                 }
4157                 else if(g_settings.getBool("give_initial_stuff"))
4158                 {
4159                         {
4160                                 InventoryItem *item = new ToolItem("SteelPick", 0);
4161                                 void* r = player->inventory.addItem("main", item);
4162                                 assert(r == NULL);
4163                         }
4164                         {
4165                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4166                                 void* r = player->inventory.addItem("main", item);
4167                                 assert(r == NULL);
4168                         }
4169                         {
4170                                 InventoryItem *item = new ToolItem("SteelAxe", 0);
4171                                 void* r = player->inventory.addItem("main", item);
4172                                 assert(r == NULL);
4173                         }
4174                         {
4175                                 InventoryItem *item = new ToolItem("SteelShovel", 0);
4176                                 void* r = player->inventory.addItem("main", item);
4177                                 assert(r == NULL);
4178                         }
4179                         {
4180                                 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4181                                 void* r = player->inventory.addItem("main", item);
4182                                 assert(r == NULL);
4183                         }
4184                         /*{
4185                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4186                                 void* r = player->inventory.addItem("main", item);
4187                                 assert(r == NULL);
4188                         }
4189                         {
4190                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4191                                 void* r = player->inventory.addItem("main", item);
4192                                 assert(r == NULL);
4193                         }
4194                         {
4195                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4196                                 void* r = player->inventory.addItem("main", item);
4197                                 assert(r == NULL);
4198                         }
4199                         {
4200                                 InventoryItem *item = new CraftItem("Stick", 4);
4201                                 void* r = player->inventory.addItem("main", item);
4202                                 assert(r == NULL);
4203                         }
4204                         {
4205                                 InventoryItem *item = new ToolItem("WPick", 32000);
4206                                 void* r = player->inventory.addItem("main", item);
4207                                 assert(r == NULL);
4208                         }
4209                         {
4210                                 InventoryItem *item = new ToolItem("STPick", 32000);
4211                                 void* r = player->inventory.addItem("main", item);
4212                                 assert(r == NULL);
4213                         }*/
4214                         /*// and some signs
4215                         for(u16 i=0; i<4; i++)
4216                         {
4217                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4218                                 bool r = player->inventory.addItem("main", item);
4219                                 assert(r == true);
4220                         }*/
4221                         /*// Give some other stuff
4222                         {
4223                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4224                                 bool r = player->inventory.addItem("main", item);
4225                                 assert(r == true);
4226                         }*/
4227                 }
4228
4229                 return player;
4230                 
4231         } // create new player
4232 }
4233
4234 void Server::handlePeerChange(PeerChange &c)
4235 {
4236         JMutexAutoLock envlock(m_env_mutex);
4237         JMutexAutoLock conlock(m_con_mutex);
4238         
4239         if(c.type == PEER_ADDED)
4240         {
4241                 /*
4242                         Add
4243                 */
4244
4245                 // Error check
4246                 core::map<u16, RemoteClient*>::Node *n;
4247                 n = m_clients.find(c.peer_id);
4248                 // The client shouldn't already exist
4249                 assert(n == NULL);
4250
4251                 // Create client
4252                 RemoteClient *client = new RemoteClient();
4253                 client->peer_id = c.peer_id;
4254                 m_clients.insert(client->peer_id, client);
4255
4256         } // PEER_ADDED
4257         else if(c.type == PEER_REMOVED)
4258         {
4259                 /*
4260                         Delete
4261                 */
4262
4263                 // Error check
4264                 core::map<u16, RemoteClient*>::Node *n;
4265                 n = m_clients.find(c.peer_id);
4266                 // The client should exist
4267                 assert(n != NULL);
4268                 
4269                 /*
4270                         Mark objects to be not known by the client
4271                 */
4272                 RemoteClient *client = n->getValue();
4273                 // Handle objects
4274                 for(core::map<u16, bool>::Iterator
4275                                 i = client->m_known_objects.getIterator();
4276                                 i.atEnd()==false; i++)
4277                 {
4278                         // Get object
4279                         u16 id = i.getNode()->getKey();
4280                         ServerActiveObject* obj = m_env.getActiveObject(id);
4281                         
4282                         if(obj && obj->m_known_by_count > 0)
4283                                 obj->m_known_by_count--;
4284                 }
4285
4286                 // Collect information about leaving in chat
4287                 std::wstring message;
4288                 {
4289                         std::wstring name = L"unknown";
4290                         Player *player = m_env.getPlayer(c.peer_id);
4291                         if(player != NULL)
4292                                 name = narrow_to_wide(player->getName());
4293                         
4294                         message += L"*** ";
4295                         message += name;
4296                         message += L" left game";
4297                         if(c.timeout)
4298                                 message += L" (timed out)";
4299                 }
4300
4301                 /*// Delete player
4302                 {
4303                         m_env.removePlayer(c.peer_id);
4304                 }*/
4305
4306                 // Set player client disconnected
4307                 {
4308                         Player *player = m_env.getPlayer(c.peer_id);
4309                         if(player != NULL)
4310                                 player->peer_id = 0;
4311                 }
4312                 
4313                 // Delete client
4314                 delete m_clients[c.peer_id];
4315                 m_clients.remove(c.peer_id);
4316
4317                 // Send player info to all remaining clients
4318                 SendPlayerInfos();
4319                 
4320                 // Send leave chat message to all remaining clients
4321                 BroadcastChatMessage(message);
4322                 
4323         } // PEER_REMOVED
4324         else
4325         {
4326                 assert(0);
4327         }
4328 }
4329
4330 void Server::handlePeerChanges()
4331 {
4332         while(m_peer_change_queue.size() > 0)
4333         {
4334                 PeerChange c = m_peer_change_queue.pop_front();
4335
4336                 dout_server<<"Server: Handling peer change: "
4337                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4338                                 <<std::endl;
4339
4340                 handlePeerChange(c);
4341         }
4342 }
4343
4344 void dedicated_server_loop(Server &server, bool &kill)
4345 {
4346         DSTACK(__FUNCTION_NAME);
4347         
4348         std::cout<<DTIME<<std::endl;
4349         std::cout<<"========================"<<std::endl;
4350         std::cout<<"Running dedicated server"<<std::endl;
4351         std::cout<<"========================"<<std::endl;
4352         std::cout<<std::endl;
4353
4354         for(;;)
4355         {
4356                 // This is kind of a hack but can be done like this
4357                 // because server.step() is very light
4358                 sleep_ms(30);
4359                 server.step(0.030);
4360
4361                 if(server.getShutdownRequested() || kill)
4362                 {
4363                         std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4364                         break;
4365                 }
4366
4367                 static int counter = 0;
4368                 counter--;
4369                 if(counter <= 0)
4370                 {
4371                         counter = 10;
4372
4373                         core::list<PlayerInfo> list = server.getPlayerInfo();
4374                         core::list<PlayerInfo>::Iterator i;
4375                         static u32 sum_old = 0;
4376                         u32 sum = PIChecksum(list);
4377                         if(sum != sum_old)
4378                         {
4379                                 std::cout<<DTIME<<"Player info:"<<std::endl;
4380                                 for(i=list.begin(); i!=list.end(); i++)
4381                                 {
4382                                         i->PrintLine(&std::cout);
4383                                 }
4384                         }
4385                         sum_old = sum;
4386                 }
4387         }
4388 }
4389
4390