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