]> git.lizzy.rs Git - minetest.git/blob - src/server.cpp
Improved server commands and added player permissions.
[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
1738                 if(datasize < 3)
1739                         return;
1740
1741                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1742                                 <<peer->id<<std::endl;
1743
1744                 // First byte after command is maximum supported
1745                 // serialization version
1746                 u8 client_max = data[2];
1747                 u8 our_max = SER_FMT_VER_HIGHEST;
1748                 // Use the highest version supported by both
1749                 u8 deployed = core::min_(client_max, our_max);
1750                 // If it's lower than the lowest supported, give up.
1751                 if(deployed < SER_FMT_VER_LOWEST)
1752                         deployed = SER_FMT_VER_INVALID;
1753
1754                 //peer->serialization_version = deployed;
1755                 getClient(peer->id)->pending_serialization_version = deployed;
1756
1757                 if(deployed == SER_FMT_VER_INVALID)
1758                 {
1759                         derr_server<<DTIME<<"Server: Cannot negotiate "
1760                                         "serialization version with peer "
1761                                         <<peer_id<<std::endl;
1762                         return;
1763                 }
1764
1765                 /*
1766                         Set up player
1767                 */
1768                 
1769                 // Get player name
1770                 const u32 playername_size = 20;
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 player
1779                 Player *player = emergePlayer(playername, "", peer_id);
1780                 //Player *player = m_env.getPlayer(peer_id);
1781
1782                 /*{
1783                         // DEBUG: Test serialization
1784                         std::ostringstream test_os;
1785                         player->serialize(test_os);
1786                         dstream<<"Player serialization test: \""<<test_os.str()
1787                                         <<"\""<<std::endl;
1788                         std::istringstream test_is(test_os.str());
1789                         player->deSerialize(test_is);
1790                 }*/
1791
1792                 // If failed, cancel
1793                 if(player == NULL)
1794                 {
1795                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1796                                         <<": failed to emerge player"<<std::endl;
1797                         return;
1798                 }
1799
1800                 /*
1801                 // If a client is already connected to the player, cancel
1802                 if(player->peer_id != 0)
1803                 {
1804                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1805                                         <<" tried to connect to "
1806                                         "an already connected player (peer_id="
1807                                         <<player->peer_id<<")"<<std::endl;
1808                         return;
1809                 }
1810                 // Set client of player
1811                 player->peer_id = peer_id;
1812                 */
1813
1814                 // Check if player doesn't exist
1815                 if(player == NULL)
1816                         throw con::InvalidIncomingDataException
1817                                 ("Server::ProcessData(): INIT: Player doesn't exist");
1818
1819                 /*// update name if it was supplied
1820                 if(datasize >= 20+3)
1821                 {
1822                         data[20+3-1] = 0;
1823                         player->updateName((const char*)&data[3]);
1824                 }*/
1825                 
1826                 /*
1827                         Answer with a TOCLIENT_INIT
1828                 */
1829                 {
1830                         SharedBuffer<u8> reply(2+1+6+8);
1831                         writeU16(&reply[0], TOCLIENT_INIT);
1832                         writeU8(&reply[2], deployed);
1833                         writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1834                         //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1835                         writeU64(&reply[2+1+6], 0); // no seed
1836                         
1837                         // Send as reliable
1838                         m_con.Send(peer_id, 0, reply, true);
1839                 }
1840
1841                 /*
1842                         Send complete position information
1843                 */
1844                 SendMovePlayer(player);
1845
1846                 return;
1847         }
1848
1849         if(command == TOSERVER_INIT2)
1850         {
1851                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1852                                 <<peer->id<<std::endl;
1853
1854
1855                 getClient(peer->id)->serialization_version
1856                                 = getClient(peer->id)->pending_serialization_version;
1857
1858                 /*
1859                         Send some initialization data
1860                 */
1861                 
1862                 // Send player info to all players
1863                 SendPlayerInfos();
1864
1865                 // Send inventory to player
1866                 UpdateCrafting(peer->id);
1867                 SendInventory(peer->id);
1868
1869                 // Send HP
1870                 {
1871                         Player *player = m_env.getPlayer(peer_id);
1872                         SendPlayerHP(player);
1873                 }
1874                 
1875                 // Send time of day
1876                 {
1877                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1878                                         m_time_of_day.get());
1879                         m_con.Send(peer->id, 0, data, true);
1880                 }
1881                 
1882                 // Send information about server to player in chat
1883                 SendChatMessage(peer_id, getStatusString());
1884                 
1885                 // Send information about joining in chat
1886                 {
1887                         std::wstring name = L"unknown";
1888                         Player *player = m_env.getPlayer(peer_id);
1889                         if(player != NULL)
1890                                 name = narrow_to_wide(player->getName());
1891                         
1892                         std::wstring message;
1893                         message += L"*** ";
1894                         message += name;
1895                         message += L" joined game";
1896                         BroadcastChatMessage(message);
1897                 }
1898
1899                 return;
1900         }
1901
1902         if(peer_ser_ver == SER_FMT_VER_INVALID)
1903         {
1904                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1905                                 " serialization format invalid or not initialized."
1906                                 " Skipping incoming command="<<command<<std::endl;
1907                 return;
1908         }
1909         
1910         Player *player = m_env.getPlayer(peer_id);
1911
1912         if(player == NULL){
1913                 derr_server<<"Server::ProcessData(): Cancelling: "
1914                                 "No player for peer_id="<<peer_id
1915                                 <<std::endl;
1916                 return;
1917         }
1918         if(command == TOSERVER_PLAYERPOS)
1919         {
1920                 if(datasize < 2+12+12+4+4)
1921                         return;
1922         
1923                 u32 start = 0;
1924                 v3s32 ps = readV3S32(&data[start+2]);
1925                 v3s32 ss = readV3S32(&data[start+2+12]);
1926                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1927                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1928                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1929                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1930                 pitch = wrapDegrees(pitch);
1931                 yaw = wrapDegrees(yaw);
1932                 player->setPosition(position);
1933                 player->setSpeed(speed);
1934                 player->setPitch(pitch);
1935                 player->setYaw(yaw);
1936                 
1937                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1938                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1939                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1940         }
1941         else if(command == TOSERVER_GOTBLOCKS)
1942         {
1943                 if(datasize < 2+1)
1944                         return;
1945                 
1946                 /*
1947                         [0] u16 command
1948                         [2] u8 count
1949                         [3] v3s16 pos_0
1950                         [3+6] v3s16 pos_1
1951                         ...
1952                 */
1953
1954                 u16 count = data[2];
1955                 for(u16 i=0; i<count; i++)
1956                 {
1957                         if((s16)datasize < 2+1+(i+1)*6)
1958                                 throw con::InvalidIncomingDataException
1959                                         ("GOTBLOCKS length is too short");
1960                         v3s16 p = readV3S16(&data[2+1+i*6]);
1961                         /*dstream<<"Server: GOTBLOCKS ("
1962                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1963                         RemoteClient *client = getClient(peer_id);
1964                         client->GotBlock(p);
1965                 }
1966         }
1967         else if(command == TOSERVER_DELETEDBLOCKS)
1968         {
1969                 if(datasize < 2+1)
1970                         return;
1971                 
1972                 /*
1973                         [0] u16 command
1974                         [2] u8 count
1975                         [3] v3s16 pos_0
1976                         [3+6] v3s16 pos_1
1977                         ...
1978                 */
1979
1980                 u16 count = data[2];
1981                 for(u16 i=0; i<count; i++)
1982                 {
1983                         if((s16)datasize < 2+1+(i+1)*6)
1984                                 throw con::InvalidIncomingDataException
1985                                         ("DELETEDBLOCKS length is too short");
1986                         v3s16 p = readV3S16(&data[2+1+i*6]);
1987                         /*dstream<<"Server: DELETEDBLOCKS ("
1988                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1989                         RemoteClient *client = getClient(peer_id);
1990                         client->SetBlockNotSent(p);
1991                 }
1992         }
1993         else if(command == TOSERVER_CLICK_OBJECT)
1994         {
1995                 if(datasize < 13)
1996                         return;
1997
1998                 if((player->privs & PRIV_BUILD) == 0)
1999                         return;
2000
2001                 /*
2002                         [0] u16 command
2003                         [2] u8 button (0=left, 1=right)
2004                         [3] v3s16 block
2005                         [9] s16 id
2006                         [11] u16 item
2007                 */
2008                 u8 button = readU8(&data[2]);
2009                 v3s16 p;
2010                 p.X = readS16(&data[3]);
2011                 p.Y = readS16(&data[5]);
2012                 p.Z = readS16(&data[7]);
2013                 s16 id = readS16(&data[9]);
2014                 //u16 item_i = readU16(&data[11]);
2015
2016                 MapBlock *block = NULL;
2017                 try
2018                 {
2019                         block = m_env.getMap().getBlockNoCreate(p);
2020                 }
2021                 catch(InvalidPositionException &e)
2022                 {
2023                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2024                         return;
2025                 }
2026
2027                 MapBlockObject *obj = block->getObject(id);
2028
2029                 if(obj == NULL)
2030                 {
2031                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2032                         return;
2033                 }
2034
2035                 //TODO: Check that object is reasonably close
2036                 
2037                 // Left click
2038                 if(button == 0)
2039                 {
2040                         InventoryList *ilist = player->inventory.getList("main");
2041                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2042                         {
2043                         
2044                                 // Skip if inventory has no free space
2045                                 if(ilist->getUsedSlots() == ilist->getSize())
2046                                 {
2047                                         dout_server<<"Player inventory has no free space"<<std::endl;
2048                                         return;
2049                                 }
2050                                 
2051                                 /*
2052                                         Create the inventory item
2053                                 */
2054                                 InventoryItem *item = NULL;
2055                                 // If it is an item-object, take the item from it
2056                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2057                                 {
2058                                         item = ((ItemObject*)obj)->createInventoryItem();
2059                                 }
2060                                 // Else create an item of the object
2061                                 else
2062                                 {
2063                                         item = new MapBlockObjectItem
2064                                                         (obj->getInventoryString());
2065                                 }
2066                                 
2067                                 // Add to inventory and send inventory
2068                                 ilist->addItem(item);
2069                                 UpdateCrafting(player->peer_id);
2070                                 SendInventory(player->peer_id);
2071                         }
2072
2073                         // Remove from block
2074                         block->removeObject(id);
2075                 }
2076         }
2077         else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2078         {
2079                 if(datasize < 7)
2080                         return;
2081
2082                 if((player->privs & PRIV_BUILD) == 0)
2083                         return;
2084
2085                 /*
2086                         length: 7
2087                         [0] u16 command
2088                         [2] u8 button (0=left, 1=right)
2089                         [3] u16 id
2090                         [5] u16 item
2091                 */
2092                 u8 button = readU8(&data[2]);
2093                 u16 id = readS16(&data[3]);
2094                 u16 item_i = readU16(&data[11]);
2095         
2096                 ServerActiveObject *obj = m_env.getActiveObject(id);
2097
2098                 if(obj == NULL)
2099                 {
2100                         derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2101                                         <<std::endl;
2102                         return;
2103                 }
2104
2105                 //TODO: Check that object is reasonably close
2106                 
2107                 // Left click, pick object up (usually)
2108                 if(button == 0)
2109                 {
2110                         InventoryList *ilist = player->inventory.getList("main");
2111                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2112                         {
2113                         
2114                                 // Skip if inventory has no free space
2115                                 if(ilist->getUsedSlots() == ilist->getSize())
2116                                 {
2117                                         dout_server<<"Player inventory has no free space"<<std::endl;
2118                                         return;
2119                                 }
2120
2121                                 // Skip if object has been removed
2122                                 if(obj->m_removed)
2123                                         return;
2124                                 
2125                                 /*
2126                                         Create the inventory item
2127                                 */
2128                                 InventoryItem *item = obj->createPickedUpItem();
2129                                 
2130                                 if(item)
2131                                 {
2132                                         // Add to inventory and send inventory
2133                                         ilist->addItem(item);
2134                                         UpdateCrafting(player->peer_id);
2135                                         SendInventory(player->peer_id);
2136
2137                                         // Remove object from environment
2138                                         obj->m_removed = true;
2139                                 }
2140                                 else
2141                                 {
2142                                         /*
2143                                                 Item cannot be picked up. Punch it instead.
2144                                         */
2145
2146                                         ToolItem *titem = NULL;
2147                                         std::string toolname = "";
2148
2149                                         InventoryList *mlist = player->inventory.getList("main");
2150                                         if(mlist != NULL)
2151                                         {
2152                                                 InventoryItem *item = mlist->getItem(item_i);
2153                                                 if(item && (std::string)item->getName() == "ToolItem")
2154                                                 {
2155                                                         titem = (ToolItem*)item;
2156                                                         toolname = titem->getToolName();
2157                                                 }
2158                                         }
2159                                         
2160                                         u16 wear = obj->punch(toolname);
2161                                         
2162                                         if(titem)
2163                                         {
2164                                                 bool weared_out = titem->addWear(wear);
2165                                                 if(weared_out)
2166                                                         mlist->deleteItem(item_i);
2167                                                 SendInventory(player->peer_id);
2168                                         }
2169                                 }
2170                         }
2171                 }
2172         }
2173         else if(command == TOSERVER_GROUND_ACTION)
2174         {
2175                 if(datasize < 17)
2176                         return;
2177                 if((player->privs & PRIV_BUILD) == 0)
2178                         return;
2179                 /*
2180                         length: 17
2181                         [0] u16 command
2182                         [2] u8 action
2183                         [3] v3s16 nodepos_undersurface
2184                         [9] v3s16 nodepos_abovesurface
2185                         [15] u16 item
2186                         actions:
2187                         0: start digging
2188                         1: place block
2189                         2: stop digging (all parameters ignored)
2190                         3: digging completed
2191                 */
2192                 u8 action = readU8(&data[2]);
2193                 v3s16 p_under;
2194                 p_under.X = readS16(&data[3]);
2195                 p_under.Y = readS16(&data[5]);
2196                 p_under.Z = readS16(&data[7]);
2197                 v3s16 p_over;
2198                 p_over.X = readS16(&data[9]);
2199                 p_over.Y = readS16(&data[11]);
2200                 p_over.Z = readS16(&data[13]);
2201                 u16 item_i = readU16(&data[15]);
2202
2203                 //TODO: Check that target is reasonably close
2204                 
2205                 /*
2206                         0: start digging
2207                 */
2208                 if(action == 0)
2209                 {
2210                         /*
2211                                 NOTE: This can be used in the future to check if
2212                                 somebody is cheating, by checking the timing.
2213                         */
2214                 } // action == 0
2215
2216                 /*
2217                         2: stop digging
2218                 */
2219                 else if(action == 2)
2220                 {
2221 #if 0
2222                         RemoteClient *client = getClient(peer->id);
2223                         JMutexAutoLock digmutex(client->m_dig_mutex);
2224                         client->m_dig_tool_item = -1;
2225 #endif
2226                 }
2227
2228                 /*
2229                         3: Digging completed
2230                 */
2231                 else if(action == 3)
2232                 {
2233                         // Mandatory parameter; actually used for nothing
2234                         core::map<v3s16, MapBlock*> modified_blocks;
2235
2236                         u8 material;
2237                         u8 mineral = MINERAL_NONE;
2238
2239                         bool cannot_remove_node = false;
2240
2241                         try
2242                         {
2243                                 MapNode n = m_env.getMap().getNode(p_under);
2244                                 // Get mineral
2245                                 mineral = n.getMineral();
2246                                 // Get material at position
2247                                 material = n.d;
2248                                 // If not yet cancelled
2249                                 if(cannot_remove_node == false)
2250                                 {
2251                                         // If it's not diggable, do nothing
2252                                         if(content_diggable(material) == false)
2253                                         {
2254                                                 derr_server<<"Server: Not finishing digging: "
2255                                                                 <<"Node not diggable"
2256                                                                 <<std::endl;
2257                                                 cannot_remove_node = true;
2258                                         }
2259                                 }
2260                                 // If not yet cancelled
2261                                 if(cannot_remove_node == false)
2262                                 {
2263                                         // Get node metadata
2264                                         NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2265                                         if(meta && meta->nodeRemovalDisabled() == true)
2266                                         {
2267                                                 derr_server<<"Server: Not finishing digging: "
2268                                                                 <<"Node metadata disables removal"
2269                                                                 <<std::endl;
2270                                                 cannot_remove_node = true;
2271                                         }
2272                                 }
2273                         }
2274                         catch(InvalidPositionException &e)
2275                         {
2276                                 derr_server<<"Server: Not finishing digging: Node not found."
2277                                                 <<" Adding block to emerge queue."
2278                                                 <<std::endl;
2279                                 m_emerge_queue.addBlock(peer_id,
2280                                                 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2281                                 cannot_remove_node = true;
2282                         }
2283
2284                         /*
2285                                 If node can't be removed, set block to be re-sent to
2286                                 client and quit.
2287                         */
2288                         if(cannot_remove_node)
2289                         {
2290                                 derr_server<<"Server: Not finishing digging."<<std::endl;
2291
2292                                 // Client probably has wrong data.
2293                                 // Set block not sent, so that client will get
2294                                 // a valid one.
2295                                 dstream<<"Client "<<peer_id<<" tried to dig "
2296                                                 <<"node; but node cannot be removed."
2297                                                 <<" setting MapBlock not sent."<<std::endl;
2298                                 RemoteClient *client = getClient(peer_id);
2299                                 v3s16 blockpos = getNodeBlockPos(p_under);
2300                                 client->SetBlockNotSent(blockpos);
2301                                         
2302                                 return;
2303                         }
2304                         
2305                         /*
2306                                 Send the removal to all other clients.
2307                                 - If other player is close, send REMOVENODE
2308                                 - Otherwise set blocks not sent
2309                         */
2310                         core::list<u16> far_players;
2311                         sendRemoveNode(p_under, peer_id, &far_players, 100);
2312                         
2313                         /*
2314                                 Update and send inventory
2315                         */
2316
2317                         if(g_settings.getBool("creative_mode") == false)
2318                         {
2319                                 /*
2320                                         Wear out tool
2321                                 */
2322                                 InventoryList *mlist = player->inventory.getList("main");
2323                                 if(mlist != NULL)
2324                                 {
2325                                         InventoryItem *item = mlist->getItem(item_i);
2326                                         if(item && (std::string)item->getName() == "ToolItem")
2327                                         {
2328                                                 ToolItem *titem = (ToolItem*)item;
2329                                                 std::string toolname = titem->getToolName();
2330
2331                                                 // Get digging properties for material and tool
2332                                                 DiggingProperties prop =
2333                                                                 getDiggingProperties(material, toolname);
2334
2335                                                 if(prop.diggable == false)
2336                                                 {
2337                                                         derr_server<<"Server: WARNING: Player digged"
2338                                                                         <<" with impossible material + tool"
2339                                                                         <<" combination"<<std::endl;
2340                                                 }
2341                                                 
2342                                                 bool weared_out = titem->addWear(prop.wear);
2343
2344                                                 if(weared_out)
2345                                                 {
2346                                                         mlist->deleteItem(item_i);
2347                                                 }
2348                                         }
2349                                 }
2350
2351                                 /*
2352                                         Add dug item to inventory
2353                                 */
2354
2355                                 InventoryItem *item = NULL;
2356
2357                                 if(mineral != MINERAL_NONE)
2358                                         item = getDiggedMineralItem(mineral);
2359                                 
2360                                 // If not mineral
2361                                 if(item == NULL)
2362                                 {
2363                                         std::string &dug_s = content_features(material).dug_item;
2364                                         if(dug_s != "")
2365                                         {
2366                                                 std::istringstream is(dug_s, std::ios::binary);
2367                                                 item = InventoryItem::deSerialize(is);
2368                                         }
2369                                 }
2370                                 
2371                                 if(item != NULL)
2372                                 {
2373                                         // Add a item to inventory
2374                                         player->inventory.addItem("main", item);
2375
2376                                         // Send inventory
2377                                         UpdateCrafting(player->peer_id);
2378                                         SendInventory(player->peer_id);
2379                                 }
2380                         }
2381
2382                         /*
2383                                 Remove the node
2384                                 (this takes some time so it is done after the quick stuff)
2385                         */
2386                         m_ignore_map_edit_events = true;
2387                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2388                         m_ignore_map_edit_events = false;
2389                         
2390                         /*
2391                                 Set blocks not sent to far players
2392                         */
2393                         for(core::list<u16>::Iterator
2394                                         i = far_players.begin();
2395                                         i != far_players.end(); i++)
2396                         {
2397                                 u16 peer_id = *i;
2398                                 RemoteClient *client = getClient(peer_id);
2399                                 if(client==NULL)
2400                                         continue;
2401                                 client->SetBlocksNotSent(modified_blocks);
2402                         }
2403                 }
2404                 
2405                 /*
2406                         1: place block
2407                 */
2408                 else if(action == 1)
2409                 {
2410
2411                         InventoryList *ilist = player->inventory.getList("main");
2412                         if(ilist == NULL)
2413                                 return;
2414
2415                         // Get item
2416                         InventoryItem *item = ilist->getItem(item_i);
2417                         
2418                         // If there is no item, it is not possible to add it anywhere
2419                         if(item == NULL)
2420                                 return;
2421                         
2422                         /*
2423                                 Handle material items
2424                         */
2425                         if(std::string("MaterialItem") == item->getName())
2426                         {
2427                                 try{
2428                                         // Don't add a node if this is not a free space
2429                                         MapNode n2 = m_env.getMap().getNode(p_over);
2430                                         if(content_buildable_to(n2.d) == false)
2431                                         {
2432                                                 // Client probably has wrong data.
2433                                                 // Set block not sent, so that client will get
2434                                                 // a valid one.
2435                                                 dstream<<"Client "<<peer_id<<" tried to place"
2436                                                                 <<" node in invalid position; setting"
2437                                                                 <<" MapBlock not sent."<<std::endl;
2438                                                 RemoteClient *client = getClient(peer_id);
2439                                                 v3s16 blockpos = getNodeBlockPos(p_over);
2440                                                 client->SetBlockNotSent(blockpos);
2441                                                 return;
2442                                         }
2443                                 }
2444                                 catch(InvalidPositionException &e)
2445                                 {
2446                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2447                                                         <<" Adding block to emerge queue."
2448                                                         <<std::endl;
2449                                         m_emerge_queue.addBlock(peer_id,
2450                                                         getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2451                                         return;
2452                                 }
2453
2454                                 // Reset build time counter
2455                                 getClient(peer->id)->m_time_from_building = 0.0;
2456                                 
2457                                 // Create node data
2458                                 MaterialItem *mitem = (MaterialItem*)item;
2459                                 MapNode n;
2460                                 n.d = mitem->getMaterial();
2461                                 if(content_features(n.d).wall_mounted)
2462                                         n.dir = packDir(p_under - p_over);
2463                                 
2464                                 /*
2465                                         Send to all players
2466                                 */
2467                                 core::list<u16> far_players;
2468                                 sendAddNode(p_over, n, 0, &far_players, 100);
2469                                 
2470                                 /*
2471                                         Handle inventory
2472                                 */
2473                                 InventoryList *ilist = player->inventory.getList("main");
2474                                 if(g_settings.getBool("creative_mode") == false && ilist)
2475                                 {
2476                                         // Remove from inventory and send inventory
2477                                         if(mitem->getCount() == 1)
2478                                                 ilist->deleteItem(item_i);
2479                                         else
2480                                                 mitem->remove(1);
2481                                         // Send inventory
2482                                         UpdateCrafting(peer_id);
2483                                         SendInventory(peer_id);
2484                                 }
2485                                 
2486                                 /*
2487                                         Add node.
2488
2489                                         This takes some time so it is done after the quick stuff
2490                                 */
2491                                 core::map<v3s16, MapBlock*> modified_blocks;
2492                                 m_ignore_map_edit_events = true;
2493                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2494                                 m_ignore_map_edit_events = false;
2495                                 
2496                                 /*
2497                                         Set blocks not sent to far players
2498                                 */
2499                                 for(core::list<u16>::Iterator
2500                                                 i = far_players.begin();
2501                                                 i != far_players.end(); i++)
2502                                 {
2503                                         u16 peer_id = *i;
2504                                         RemoteClient *client = getClient(peer_id);
2505                                         if(client==NULL)
2506                                                 continue;
2507                                         client->SetBlocksNotSent(modified_blocks);
2508                                 }
2509
2510                                 /*
2511                                         Calculate special events
2512                                 */
2513                                 
2514                                 /*if(n.d == CONTENT_MESE)
2515                                 {
2516                                         u32 count = 0;
2517                                         for(s16 z=-1; z<=1; z++)
2518                                         for(s16 y=-1; y<=1; y++)
2519                                         for(s16 x=-1; x<=1; x++)
2520                                         {
2521                                                 
2522                                         }
2523                                 }*/
2524                         }
2525                         /*
2526                                 Place other item (not a block)
2527                         */
2528                         else
2529                         {
2530                                 v3s16 blockpos = getNodeBlockPos(p_over);
2531                                 
2532                                 /*
2533                                         Check that the block is loaded so that the item
2534                                         can properly be added to the static list too
2535                                 */
2536                                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2537                                 if(block==NULL)
2538                                 {
2539                                         derr_server<<"Error while placing object: "
2540                                                         "block not found"<<std::endl;
2541                                         return;
2542                                 }
2543
2544                                 dout_server<<"Placing a miscellaneous item on map"
2545                                                 <<std::endl;
2546                                 
2547                                 // Calculate a position for it
2548                                 v3f pos = intToFloat(p_over, BS);
2549                                 //pos.Y -= BS*0.45;
2550                                 pos.Y -= BS*0.25; // let it drop a bit
2551                                 // Randomize a bit
2552                                 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2553                                 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2554
2555                                 /*
2556                                         Create the object
2557                                 */
2558                                 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2559
2560                                 if(obj == NULL)
2561                                 {
2562                                         derr_server<<"WARNING: item resulted in NULL object, "
2563                                                         <<"not placing onto map"
2564                                                         <<std::endl;
2565                                 }
2566                                 else
2567                                 {
2568                                         // Add the object to the environment
2569                                         m_env.addActiveObject(obj);
2570                                         
2571                                         dout_server<<"Placed object"<<std::endl;
2572
2573                                         if(g_settings.getBool("creative_mode") == false)
2574                                         {
2575                                                 // Delete the right amount of items from the slot
2576                                                 u16 dropcount = item->getDropCount();
2577                                                 
2578                                                 // Delete item if all gone
2579                                                 if(item->getCount() <= dropcount)
2580                                                 {
2581                                                         if(item->getCount() < dropcount)
2582                                                                 dstream<<"WARNING: Server: dropped more items"
2583                                                                                 <<" than the slot contains"<<std::endl;
2584                                                         
2585                                                         InventoryList *ilist = player->inventory.getList("main");
2586                                                         if(ilist)
2587                                                                 // Remove from inventory and send inventory
2588                                                                 ilist->deleteItem(item_i);
2589                                                 }
2590                                                 // Else decrement it
2591                                                 else
2592                                                         item->remove(dropcount);
2593                                                 
2594                                                 // Send inventory
2595                                                 UpdateCrafting(peer_id);
2596                                                 SendInventory(peer_id);
2597                                         }
2598                                 }
2599                         }
2600
2601                 } // action == 1
2602
2603                 /*
2604                         Catch invalid actions
2605                 */
2606                 else
2607                 {
2608                         derr_server<<"WARNING: Server: Invalid action "
2609                                         <<action<<std::endl;
2610                 }
2611         }
2612 #if 0
2613         else if(command == TOSERVER_RELEASE)
2614         {
2615                 if(datasize < 3)
2616                         return;
2617                 /*
2618                         length: 3
2619                         [0] u16 command
2620                         [2] u8 button
2621                 */
2622                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2623         }
2624 #endif
2625         else if(command == TOSERVER_SIGNTEXT)
2626         {
2627                 if((player->privs & PRIV_BUILD) == 0)
2628                         return;
2629                 /*
2630                         u16 command
2631                         v3s16 blockpos
2632                         s16 id
2633                         u16 textlen
2634                         textdata
2635                 */
2636                 std::string datastring((char*)&data[2], datasize-2);
2637                 std::istringstream is(datastring, std::ios_base::binary);
2638                 u8 buf[6];
2639                 // Read stuff
2640                 is.read((char*)buf, 6);
2641                 v3s16 blockpos = readV3S16(buf);
2642                 is.read((char*)buf, 2);
2643                 s16 id = readS16(buf);
2644                 is.read((char*)buf, 2);
2645                 u16 textlen = readU16(buf);
2646                 std::string text;
2647                 for(u16 i=0; i<textlen; i++)
2648                 {
2649                         is.read((char*)buf, 1);
2650                         text += (char)buf[0];
2651                 }
2652
2653                 MapBlock *block = NULL;
2654                 try
2655                 {
2656                         block = m_env.getMap().getBlockNoCreate(blockpos);
2657                 }
2658                 catch(InvalidPositionException &e)
2659                 {
2660                         derr_server<<"Error while setting sign text: "
2661                                         "block not found"<<std::endl;
2662                         return;
2663                 }
2664
2665                 MapBlockObject *obj = block->getObject(id);
2666                 if(obj == NULL)
2667                 {
2668                         derr_server<<"Error while setting sign text: "
2669                                         "object not found"<<std::endl;
2670                         return;
2671                 }
2672                 
2673                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2674                 {
2675                         derr_server<<"Error while setting sign text: "
2676                                         "object is not a sign"<<std::endl;
2677                         return;
2678                 }
2679
2680                 ((SignObject*)obj)->setText(text);
2681
2682                 obj->getBlock()->setChangedFlag();
2683         }
2684         else if(command == TOSERVER_SIGNNODETEXT)
2685         {
2686                 if((player->privs & PRIV_BUILD) == 0)
2687                         return;
2688                 /*
2689                         u16 command
2690                         v3s16 p
2691                         u16 textlen
2692                         textdata
2693                 */
2694                 std::string datastring((char*)&data[2], datasize-2);
2695                 std::istringstream is(datastring, std::ios_base::binary);
2696                 u8 buf[6];
2697                 // Read stuff
2698                 is.read((char*)buf, 6);
2699                 v3s16 p = readV3S16(buf);
2700                 is.read((char*)buf, 2);
2701                 u16 textlen = readU16(buf);
2702                 std::string text;
2703                 for(u16 i=0; i<textlen; i++)
2704                 {
2705                         is.read((char*)buf, 1);
2706                         text += (char)buf[0];
2707                 }
2708
2709                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2710                 if(!meta)
2711                         return;
2712                 if(meta->typeId() != CONTENT_SIGN_WALL)
2713                         return;
2714                 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2715                 signmeta->setText(text);
2716                 
2717                 v3s16 blockpos = getNodeBlockPos(p);
2718                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2719                 if(block)
2720                 {
2721                         block->setChangedFlag();
2722                 }
2723
2724                 for(core::map<u16, RemoteClient*>::Iterator
2725                         i = m_clients.getIterator();
2726                         i.atEnd()==false; i++)
2727                 {
2728                         RemoteClient *client = i.getNode()->getValue();
2729                         client->SetBlockNotSent(blockpos);
2730                 }
2731         }
2732         else if(command == TOSERVER_INVENTORY_ACTION)
2733         {
2734                 /*// Ignore inventory changes if in creative mode
2735                 if(g_settings.getBool("creative_mode") == true)
2736                 {
2737                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2738                                         <<std::endl;
2739                         return;
2740                 }*/
2741                 // Strip command and create a stream
2742                 std::string datastring((char*)&data[2], datasize-2);
2743                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2744                 std::istringstream is(datastring, std::ios_base::binary);
2745                 // Create an action
2746                 InventoryAction *a = InventoryAction::deSerialize(is);
2747                 if(a != NULL)
2748                 {
2749                         // Create context
2750                         InventoryContext c;
2751                         c.current_player = player;
2752
2753                         /*
2754                                 Handle craftresult specially if not in creative mode
2755                         */
2756                         bool disable_action = false;
2757                         if(a->getType() == IACTION_MOVE
2758                                         && g_settings.getBool("creative_mode") == false)
2759                         {
2760                                 IMoveAction *ma = (IMoveAction*)a;
2761                                 if(ma->to_inv == "current_player" &&
2762                                                 ma->from_inv == "current_player")
2763                                 {
2764                                         InventoryList *rlist = player->inventory.getList("craftresult");
2765                                         assert(rlist);
2766                                         InventoryList *clist = player->inventory.getList("craft");
2767                                         assert(clist);
2768                                         InventoryList *mlist = player->inventory.getList("main");
2769                                         assert(mlist);
2770                                         /*
2771                                                 Craftresult is no longer preview if something
2772                                                 is moved into it
2773                                         */
2774                                         if(ma->to_list == "craftresult"
2775                                                         && ma->from_list != "craftresult")
2776                                         {
2777                                                 // If it currently is a preview, remove
2778                                                 // its contents
2779                                                 if(player->craftresult_is_preview)
2780                                                 {
2781                                                         rlist->deleteItem(0);
2782                                                 }
2783                                                 player->craftresult_is_preview = false;
2784                                         }
2785                                         /*
2786                                                 Crafting takes place if this condition is true.
2787                                         */
2788                                         if(player->craftresult_is_preview &&
2789                                                         ma->from_list == "craftresult")
2790                                         {
2791                                                 player->craftresult_is_preview = false;
2792                                                 clist->decrementMaterials(1);
2793                                         }
2794                                         /*
2795                                                 If the craftresult is placed on itself, move it to
2796                                                 main inventory instead of doing the action
2797                                         */
2798                                         if(ma->to_list == "craftresult"
2799                                                         && ma->from_list == "craftresult")
2800                                         {
2801                                                 disable_action = true;
2802                                                 
2803                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
2804                                                 mlist->addItem(item1);
2805                                         }
2806                                 }
2807                         }
2808                         
2809                         if(disable_action == false)
2810                         {
2811                                 // Feed action to player inventory
2812                                 a->apply(&c, this);
2813                                 // Eat the action
2814                                 delete a;
2815                         }
2816                         else
2817                         {
2818                                 // Send inventory
2819                                 UpdateCrafting(player->peer_id);
2820                                 SendInventory(player->peer_id);
2821                         }
2822                 }
2823                 else
2824                 {
2825                         dstream<<"TOSERVER_INVENTORY_ACTION: "
2826                                         <<"InventoryAction::deSerialize() returned NULL"
2827                                         <<std::endl;
2828                 }
2829         }
2830         else if(command == TOSERVER_CHAT_MESSAGE)
2831         {
2832                 /*
2833                         u16 command
2834                         u16 length
2835                         wstring message
2836                 */
2837                 u8 buf[6];
2838                 std::string datastring((char*)&data[2], datasize-2);
2839                 std::istringstream is(datastring, std::ios_base::binary);
2840                 
2841                 // Read stuff
2842                 is.read((char*)buf, 2);
2843                 u16 len = readU16(buf);
2844                 
2845                 std::wstring message;
2846                 for(u16 i=0; i<len; i++)
2847                 {
2848                         is.read((char*)buf, 2);
2849                         message += (wchar_t)readU16(buf);
2850                 }
2851
2852                 // Get player name of this client
2853                 std::wstring name = narrow_to_wide(player->getName());
2854                 
2855                 // Line to send to players
2856                 std::wstring line;
2857                 // Whether to send to the player that sent the line
2858                 bool send_to_sender = false;
2859                 // Whether to send to other players
2860                 bool send_to_others = false;
2861                 
2862                 // Parse commands
2863                 std::wstring commandprefix = L"/#";
2864                 if(message.substr(0, commandprefix.size()) == commandprefix)
2865                 {
2866                         line += L"Server: ";
2867
2868                         message = message.substr(commandprefix.size());
2869
2870                         ServerCommandContext *ctx = new ServerCommandContext(
2871                                 str_split(message, L' '),
2872                                 this,
2873                                 &m_env,
2874                                 player
2875                                 );
2876
2877                         line += ServerCommand::processCommand(ctx);
2878                         send_to_sender = ctx->flags & 1;
2879                         send_to_others = ctx->flags & 2;
2880                         delete ctx;
2881
2882                 }
2883                 else
2884                 {
2885                         line += L"<";
2886                         /*if(is_operator)
2887                                 line += L"@";*/
2888                         line += name;
2889                         line += L"> ";
2890                         line += message;
2891                         send_to_others = true;
2892                 }
2893                 
2894                 if(line != L"")
2895                 {
2896                         dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2897
2898                         /*
2899                                 Send the message to clients
2900                         */
2901                         for(core::map<u16, RemoteClient*>::Iterator
2902                                 i = m_clients.getIterator();
2903                                 i.atEnd() == false; i++)
2904                         {
2905                                 // Get client and check that it is valid
2906                                 RemoteClient *client = i.getNode()->getValue();
2907                                 assert(client->peer_id == i.getNode()->getKey());
2908                                 if(client->serialization_version == SER_FMT_VER_INVALID)
2909                                         continue;
2910
2911                                 // Filter recipient
2912                                 bool sender_selected = (peer_id == client->peer_id);
2913                                 if(sender_selected == true && send_to_sender == false)
2914                                         continue;
2915                                 if(sender_selected == false && send_to_others == false)
2916                                         continue;
2917
2918                                 SendChatMessage(client->peer_id, line);
2919                         }
2920                 }
2921         }
2922         else if(command == TOSERVER_DAMAGE)
2923         {
2924                 if(g_settings.getBool("enable_damage"))
2925                 {
2926                         std::string datastring((char*)&data[2], datasize-2);
2927                         std::istringstream is(datastring, std::ios_base::binary);
2928                         u8 damage = readU8(is);
2929                         if(player->hp > damage)
2930                         {
2931                                 player->hp -= damage;
2932                         }
2933                         else
2934                         {
2935                                 player->hp = 0;
2936
2937                                 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2938                                                 <<std::endl;
2939                                 
2940                                 v3f pos = findSpawnPos(m_env.getServerMap());
2941                                 player->setPosition(pos);
2942                                 player->hp = 20;
2943                                 SendMovePlayer(player);
2944                                 SendPlayerHP(player);
2945                                 
2946                                 //TODO: Throw items around
2947                         }
2948                 }
2949
2950                 SendPlayerHP(player);
2951         }
2952         else
2953         {
2954                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2955                                 "unknown command "<<command<<std::endl;
2956         }
2957         
2958         } //try
2959         catch(SendFailedException &e)
2960         {
2961                 derr_server<<"Server::ProcessData(): SendFailedException: "
2962                                 <<"what="<<e.what()
2963                                 <<std::endl;
2964         }
2965 }
2966
2967 void Server::onMapEditEvent(MapEditEvent *event)
2968 {
2969         dstream<<"Server::onMapEditEvent()"<<std::endl;
2970         if(m_ignore_map_edit_events)
2971                 return;
2972         MapEditEvent *e = event->clone();
2973         m_unsent_map_edit_queue.push_back(e);
2974 }
2975
2976 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2977 {
2978         if(id == "current_player")
2979         {
2980                 assert(c->current_player);
2981                 return &(c->current_player->inventory);
2982         }
2983         
2984         Strfnd fn(id);
2985         std::string id0 = fn.next(":");
2986
2987         if(id0 == "nodemeta")
2988         {
2989                 v3s16 p;
2990                 p.X = stoi(fn.next(","));
2991                 p.Y = stoi(fn.next(","));
2992                 p.Z = stoi(fn.next(","));
2993                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2994                 if(meta)
2995                         return meta->getInventory();
2996                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2997                                 <<"no metadata found"<<std::endl;
2998                 return NULL;
2999         }
3000
3001         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3002         return NULL;
3003 }
3004 void Server::inventoryModified(InventoryContext *c, std::string id)
3005 {
3006         if(id == "current_player")
3007         {
3008                 assert(c->current_player);
3009                 // Send inventory
3010                 UpdateCrafting(c->current_player->peer_id);
3011                 SendInventory(c->current_player->peer_id);
3012                 return;
3013         }
3014         
3015         Strfnd fn(id);
3016         std::string id0 = fn.next(":");
3017
3018         if(id0 == "nodemeta")
3019         {
3020                 v3s16 p;
3021                 p.X = stoi(fn.next(","));
3022                 p.Y = stoi(fn.next(","));
3023                 p.Z = stoi(fn.next(","));
3024                 v3s16 blockpos = getNodeBlockPos(p);
3025
3026                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3027                 if(meta)
3028                         meta->inventoryModified();
3029
3030                 for(core::map<u16, RemoteClient*>::Iterator
3031                         i = m_clients.getIterator();
3032                         i.atEnd()==false; i++)
3033                 {
3034                         RemoteClient *client = i.getNode()->getValue();
3035                         client->SetBlockNotSent(blockpos);
3036                 }
3037
3038                 return;
3039         }
3040
3041         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3042 }
3043
3044 core::list<PlayerInfo> Server::getPlayerInfo()
3045 {
3046         DSTACK(__FUNCTION_NAME);
3047         JMutexAutoLock envlock(m_env_mutex);
3048         JMutexAutoLock conlock(m_con_mutex);
3049         
3050         core::list<PlayerInfo> list;
3051
3052         core::list<Player*> players = m_env.getPlayers();
3053         
3054         core::list<Player*>::Iterator i;
3055         for(i = players.begin();
3056                         i != players.end(); i++)
3057         {
3058                 PlayerInfo info;
3059
3060                 Player *player = *i;
3061
3062                 try{
3063                         con::Peer *peer = m_con.GetPeer(player->peer_id);
3064                         // Copy info from peer to info struct
3065                         info.id = peer->id;
3066                         info.address = peer->address;
3067                         info.avg_rtt = peer->avg_rtt;
3068                 }
3069                 catch(con::PeerNotFoundException &e)
3070                 {
3071                         // Set dummy peer info
3072                         info.id = 0;
3073                         info.address = Address(0,0,0,0,0);
3074                         info.avg_rtt = 0.0;
3075                 }
3076
3077                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3078                 info.position = player->getPosition();
3079
3080                 list.push_back(info);
3081         }
3082
3083         return list;
3084 }
3085
3086
3087 void Server::peerAdded(con::Peer *peer)
3088 {
3089         DSTACK(__FUNCTION_NAME);
3090         dout_server<<"Server::peerAdded(): peer->id="
3091                         <<peer->id<<std::endl;
3092         
3093         PeerChange c;
3094         c.type = PEER_ADDED;
3095         c.peer_id = peer->id;
3096         c.timeout = false;
3097         m_peer_change_queue.push_back(c);
3098 }
3099
3100 void Server::deletingPeer(con::Peer *peer, bool timeout)
3101 {
3102         DSTACK(__FUNCTION_NAME);
3103         dout_server<<"Server::deletingPeer(): peer->id="
3104                         <<peer->id<<", timeout="<<timeout<<std::endl;
3105         
3106         PeerChange c;
3107         c.type = PEER_REMOVED;
3108         c.peer_id = peer->id;
3109         c.timeout = timeout;
3110         m_peer_change_queue.push_back(c);
3111 }
3112
3113 /*
3114         Static send methods
3115 */
3116
3117 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3118 {
3119         DSTACK(__FUNCTION_NAME);
3120         std::ostringstream os(std::ios_base::binary);
3121
3122         writeU16(os, TOCLIENT_HP);
3123         writeU8(os, hp);
3124
3125         // Make data buffer
3126         std::string s = os.str();
3127         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3128         // Send as reliable
3129         con.Send(peer_id, 0, data, true);
3130 }
3131
3132 /*
3133         Non-static send methods
3134 */
3135
3136 void Server::SendObjectData(float dtime)
3137 {
3138         DSTACK(__FUNCTION_NAME);
3139
3140         core::map<v3s16, bool> stepped_blocks;
3141         
3142         for(core::map<u16, RemoteClient*>::Iterator
3143                 i = m_clients.getIterator();
3144                 i.atEnd() == false; i++)
3145         {
3146                 u16 peer_id = i.getNode()->getKey();
3147                 RemoteClient *client = i.getNode()->getValue();
3148                 assert(client->peer_id == peer_id);
3149                 
3150                 if(client->serialization_version == SER_FMT_VER_INVALID)
3151                         continue;
3152                 
3153                 client->SendObjectData(this, dtime, stepped_blocks);
3154         }
3155 }
3156
3157 void Server::SendPlayerInfos()
3158 {
3159         DSTACK(__FUNCTION_NAME);
3160
3161         //JMutexAutoLock envlock(m_env_mutex);
3162         
3163         // Get connected players
3164         core::list<Player*> players = m_env.getPlayers(true);
3165         
3166         u32 player_count = players.getSize();
3167         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3168
3169         SharedBuffer<u8> data(datasize);
3170         writeU16(&data[0], TOCLIENT_PLAYERINFO);
3171         
3172         u32 start = 2;
3173         core::list<Player*>::Iterator i;
3174         for(i = players.begin();
3175                         i != players.end(); i++)
3176         {
3177                 Player *player = *i;
3178
3179                 /*dstream<<"Server sending player info for player with "
3180                                 "peer_id="<<player->peer_id<<std::endl;*/
3181                 
3182                 writeU16(&data[start], player->peer_id);
3183                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3184                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3185                 start += 2+PLAYERNAME_SIZE;
3186         }
3187
3188         //JMutexAutoLock conlock(m_con_mutex);
3189
3190         // Send as reliable
3191         m_con.SendToAll(0, data, true);
3192 }
3193
3194 void Server::SendInventory(u16 peer_id)
3195 {
3196         DSTACK(__FUNCTION_NAME);
3197         
3198         Player* player = m_env.getPlayer(peer_id);
3199         assert(player);
3200
3201         /*
3202                 Serialize it
3203         */
3204
3205         std::ostringstream os;
3206         //os.imbue(std::locale("C"));
3207
3208         player->inventory.serialize(os);
3209
3210         std::string s = os.str();
3211         
3212         SharedBuffer<u8> data(s.size()+2);
3213         writeU16(&data[0], TOCLIENT_INVENTORY);
3214         memcpy(&data[2], s.c_str(), s.size());
3215         
3216         // Send as reliable
3217         m_con.Send(peer_id, 0, data, true);
3218 }
3219
3220 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3221 {
3222         DSTACK(__FUNCTION_NAME);
3223         
3224         std::ostringstream os(std::ios_base::binary);
3225         u8 buf[12];
3226         
3227         // Write command
3228         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3229         os.write((char*)buf, 2);
3230         
3231         // Write length
3232         writeU16(buf, message.size());
3233         os.write((char*)buf, 2);
3234         
3235         // Write string
3236         for(u32 i=0; i<message.size(); i++)
3237         {
3238                 u16 w = message[i];
3239                 writeU16(buf, w);
3240                 os.write((char*)buf, 2);
3241         }
3242         
3243         // Make data buffer
3244         std::string s = os.str();
3245         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3246         // Send as reliable
3247         m_con.Send(peer_id, 0, data, true);
3248 }
3249
3250 void Server::BroadcastChatMessage(const std::wstring &message)
3251 {
3252         for(core::map<u16, RemoteClient*>::Iterator
3253                 i = m_clients.getIterator();
3254                 i.atEnd() == false; i++)
3255         {
3256                 // Get client and check that it is valid
3257                 RemoteClient *client = i.getNode()->getValue();
3258                 assert(client->peer_id == i.getNode()->getKey());
3259                 if(client->serialization_version == SER_FMT_VER_INVALID)
3260                         continue;
3261
3262                 SendChatMessage(client->peer_id, message);
3263         }
3264 }
3265
3266 void Server::SendPlayerHP(Player *player)
3267 {
3268         SendHP(m_con, player->peer_id, player->hp);
3269 }
3270
3271 void Server::SendMovePlayer(Player *player)
3272 {
3273         DSTACK(__FUNCTION_NAME);
3274         std::ostringstream os(std::ios_base::binary);
3275
3276         writeU16(os, TOCLIENT_MOVE_PLAYER);
3277         writeV3F1000(os, player->getPosition());
3278         writeF1000(os, player->getPitch());
3279         writeF1000(os, player->getYaw());
3280         
3281         {
3282                 v3f pos = player->getPosition();
3283                 f32 pitch = player->getPitch();
3284                 f32 yaw = player->getYaw();
3285                 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3286                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3287                                 <<" pitch="<<pitch
3288                                 <<" yaw="<<yaw
3289                                 <<std::endl;
3290         }
3291
3292         // Make data buffer
3293         std::string s = os.str();
3294         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3295         // Send as reliable
3296         m_con.Send(player->peer_id, 0, data, true);
3297 }
3298
3299 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3300         core::list<u16> *far_players, float far_d_nodes)
3301 {
3302         float maxd = far_d_nodes*BS;
3303         v3f p_f = intToFloat(p, BS);
3304
3305         // Create packet
3306         u32 replysize = 8;
3307         SharedBuffer<u8> reply(replysize);
3308         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3309         writeS16(&reply[2], p.X);
3310         writeS16(&reply[4], p.Y);
3311         writeS16(&reply[6], p.Z);
3312
3313         for(core::map<u16, RemoteClient*>::Iterator
3314                 i = m_clients.getIterator();
3315                 i.atEnd() == false; i++)
3316         {
3317                 // Get client and check that it is valid
3318                 RemoteClient *client = i.getNode()->getValue();
3319                 assert(client->peer_id == i.getNode()->getKey());
3320                 if(client->serialization_version == SER_FMT_VER_INVALID)
3321                         continue;
3322
3323                 // Don't send if it's the same one
3324                 if(client->peer_id == ignore_id)
3325                         continue;
3326                 
3327                 if(far_players)
3328                 {
3329                         // Get player
3330                         Player *player = m_env.getPlayer(client->peer_id);
3331                         if(player)
3332                         {
3333                                 // If player is far away, only set modified blocks not sent
3334                                 v3f player_pos = player->getPosition();
3335                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3336                                 {
3337                                         far_players->push_back(client->peer_id);
3338                                         continue;
3339                                 }
3340                         }
3341                 }
3342
3343                 // Send as reliable
3344                 m_con.Send(client->peer_id, 0, reply, true);
3345         }
3346 }
3347
3348 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3349                 core::list<u16> *far_players, float far_d_nodes)
3350 {
3351         float maxd = far_d_nodes*BS;
3352         v3f p_f = intToFloat(p, BS);
3353
3354         for(core::map<u16, RemoteClient*>::Iterator
3355                 i = m_clients.getIterator();
3356                 i.atEnd() == false; i++)
3357         {
3358                 // Get client and check that it is valid
3359                 RemoteClient *client = i.getNode()->getValue();
3360                 assert(client->peer_id == i.getNode()->getKey());
3361                 if(client->serialization_version == SER_FMT_VER_INVALID)
3362                         continue;
3363
3364                 // Don't send if it's the same one
3365                 if(client->peer_id == ignore_id)
3366                         continue;
3367
3368                 if(far_players)
3369                 {
3370                         // Get player
3371                         Player *player = m_env.getPlayer(client->peer_id);
3372                         if(player)
3373                         {
3374                                 // If player is far away, only set modified blocks not sent
3375                                 v3f player_pos = player->getPosition();
3376                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3377                                 {
3378                                         far_players->push_back(client->peer_id);
3379                                         continue;
3380                                 }
3381                         }
3382                 }
3383
3384                 // Create packet
3385                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3386                 SharedBuffer<u8> reply(replysize);
3387                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3388                 writeS16(&reply[2], p.X);
3389                 writeS16(&reply[4], p.Y);
3390                 writeS16(&reply[6], p.Z);
3391                 n.serialize(&reply[8], client->serialization_version);
3392
3393                 // Send as reliable
3394                 m_con.Send(client->peer_id, 0, reply, true);
3395         }
3396 }
3397
3398 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3399 {
3400         DSTACK(__FUNCTION_NAME);
3401
3402         v3s16 p = block->getPos();
3403         
3404 #if 0
3405         // Analyze it a bit
3406         bool completely_air = true;
3407         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3408         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3409         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3410         {
3411                 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3412                 {
3413                         completely_air = false;
3414                         x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3415                 }
3416         }
3417
3418         // Print result
3419         dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3420         if(completely_air)
3421                 dstream<<"[completely air] ";
3422         dstream<<std::endl;
3423 #endif
3424
3425         /*
3426                 Create a packet with the block in the right format
3427         */
3428         
3429         std::ostringstream os(std::ios_base::binary);
3430         block->serialize(os, ver);
3431         std::string s = os.str();
3432         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3433
3434         u32 replysize = 8 + blockdata.getSize();
3435         SharedBuffer<u8> reply(replysize);
3436         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3437         writeS16(&reply[2], p.X);
3438         writeS16(&reply[4], p.Y);
3439         writeS16(&reply[6], p.Z);
3440         memcpy(&reply[8], *blockdata, blockdata.getSize());
3441
3442         /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3443                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3444         
3445         /*
3446                 Send packet
3447         */
3448         m_con.Send(peer_id, 1, reply, true);
3449 }
3450
3451 void Server::SendBlocks(float dtime)
3452 {
3453         DSTACK(__FUNCTION_NAME);
3454
3455         JMutexAutoLock envlock(m_env_mutex);
3456         JMutexAutoLock conlock(m_con_mutex);
3457
3458         //TimeTaker timer("Server::SendBlocks");
3459
3460         core::array<PrioritySortedBlockTransfer> queue;
3461
3462         s32 total_sending = 0;
3463
3464         for(core::map<u16, RemoteClient*>::Iterator
3465                 i = m_clients.getIterator();
3466                 i.atEnd() == false; i++)
3467         {
3468                 RemoteClient *client = i.getNode()->getValue();
3469                 assert(client->peer_id == i.getNode()->getKey());
3470
3471                 total_sending += client->SendingCount();
3472                 
3473                 if(client->serialization_version == SER_FMT_VER_INVALID)
3474                         continue;
3475                 
3476                 client->GetNextBlocks(this, dtime, queue);
3477         }
3478
3479         // Sort.
3480         // Lowest priority number comes first.
3481         // Lowest is most important.
3482         queue.sort();
3483
3484         for(u32 i=0; i<queue.size(); i++)
3485         {
3486                 //TODO: Calculate limit dynamically
3487                 if(total_sending >= g_settings.getS32
3488                                 ("max_simultaneous_block_sends_server_total"))
3489                         break;
3490                 
3491                 PrioritySortedBlockTransfer q = queue[i];
3492
3493                 MapBlock *block = NULL;
3494                 try
3495                 {
3496                         block = m_env.getMap().getBlockNoCreate(q.pos);
3497                 }
3498                 catch(InvalidPositionException &e)
3499                 {
3500                         continue;
3501                 }
3502
3503                 RemoteClient *client = getClient(q.peer_id);
3504
3505                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3506
3507                 client->SentBlock(q.pos);
3508
3509                 total_sending++;
3510         }
3511 }
3512
3513 /*
3514         Something random
3515 */
3516
3517 void Server::UpdateCrafting(u16 peer_id)
3518 {
3519         DSTACK(__FUNCTION_NAME);
3520         
3521         Player* player = m_env.getPlayer(peer_id);
3522         assert(player);
3523
3524         /*
3525                 Calculate crafting stuff
3526         */
3527         if(g_settings.getBool("creative_mode") == false)
3528         {
3529                 InventoryList *clist = player->inventory.getList("craft");
3530                 InventoryList *rlist = player->inventory.getList("craftresult");
3531
3532                 if(rlist->getUsedSlots() == 0)
3533                         player->craftresult_is_preview = true;
3534
3535                 if(rlist && player->craftresult_is_preview)
3536                 {
3537                         rlist->clearItems();
3538                 }
3539                 if(clist && rlist && player->craftresult_is_preview)
3540                 {
3541                         InventoryItem *items[9];
3542                         for(u16 i=0; i<9; i++)
3543                         {
3544                                 items[i] = clist->getItem(i);
3545                         }
3546                         
3547                         bool found = false;
3548
3549                         // Wood
3550                         if(!found)
3551                         {
3552                                 ItemSpec specs[9];
3553                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3554                                 if(checkItemCombination(items, specs))
3555                                 {
3556                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3557                                         found = true;
3558                                 }
3559                         }
3560
3561                         // Stick
3562                         if(!found)
3563                         {
3564                                 ItemSpec specs[9];
3565                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3566                                 if(checkItemCombination(items, specs))
3567                                 {
3568                                         rlist->addItem(new CraftItem("Stick", 4));
3569                                         found = true;
3570                                 }
3571                         }
3572
3573                         // Sign
3574                         if(!found)
3575                         {
3576                                 ItemSpec specs[9];
3577                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3578                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3579                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3580                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3581                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3582                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3583                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3584                                 if(checkItemCombination(items, specs))
3585                                 {
3586                                         //rlist->addItem(new MapBlockObjectItem("Sign"));
3587                                         rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3588                                         found = true;
3589                                 }
3590                         }
3591
3592                         // Torch
3593                         if(!found)
3594                         {
3595                                 ItemSpec specs[9];
3596                                 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3597                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3598                                 if(checkItemCombination(items, specs))
3599                                 {
3600                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3601                                         found = true;
3602                                 }
3603                         }
3604
3605                         // Wooden pick
3606                         if(!found)
3607                         {
3608                                 ItemSpec specs[9];
3609                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3610                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3611                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3612                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3613                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3614                                 if(checkItemCombination(items, specs))
3615                                 {
3616                                         rlist->addItem(new ToolItem("WPick", 0));
3617                                         found = true;
3618                                 }
3619                         }
3620
3621                         // Stone pick
3622                         if(!found)
3623                         {
3624                                 ItemSpec specs[9];
3625                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3626                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3627                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3628                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3629                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3630                                 if(checkItemCombination(items, specs))
3631                                 {
3632                                         rlist->addItem(new ToolItem("STPick", 0));
3633                                         found = true;
3634                                 }
3635                         }
3636
3637                         // Steel pick
3638                         if(!found)
3639                         {
3640                                 ItemSpec specs[9];
3641                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3642                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3643                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3644                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3645                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3646                                 if(checkItemCombination(items, specs))
3647                                 {
3648                                         rlist->addItem(new ToolItem("SteelPick", 0));
3649                                         found = true;
3650                                 }
3651                         }
3652
3653                         // Mese pick
3654                         if(!found)
3655                         {
3656                                 ItemSpec specs[9];
3657                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3658                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3659                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
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("MesePick", 0));
3665                                         found = true;
3666                                 }
3667                         }
3668
3669                         // Wooden shovel
3670                         if(!found)
3671                         {
3672                                 ItemSpec specs[9];
3673                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3674                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3675                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3676                                 if(checkItemCombination(items, specs))
3677                                 {
3678                                         rlist->addItem(new ToolItem("WShovel", 0));
3679                                         found = true;
3680                                 }
3681                         }
3682
3683                         // Stone shovel
3684                         if(!found)
3685                         {
3686                                 ItemSpec specs[9];
3687                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3688                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3689                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3690                                 if(checkItemCombination(items, specs))
3691                                 {
3692                                         rlist->addItem(new ToolItem("STShovel", 0));
3693                                         found = true;
3694                                 }
3695                         }
3696
3697                         // Steel shovel
3698                         if(!found)
3699                         {
3700                                 ItemSpec specs[9];
3701                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3702                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3703                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3704                                 if(checkItemCombination(items, specs))
3705                                 {
3706                                         rlist->addItem(new ToolItem("SteelShovel", 0));
3707                                         found = true;
3708                                 }
3709                         }
3710
3711                         // Wooden axe
3712                         if(!found)
3713                         {
3714                                 ItemSpec specs[9];
3715                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3716                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3717                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3718                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3719                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3720                                 if(checkItemCombination(items, specs))
3721                                 {
3722                                         rlist->addItem(new ToolItem("WAxe", 0));
3723                                         found = true;
3724                                 }
3725                         }
3726
3727                         // Stone axe
3728                         if(!found)
3729                         {
3730                                 ItemSpec specs[9];
3731                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3732                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3733                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3734                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3735                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3736                                 if(checkItemCombination(items, specs))
3737                                 {
3738                                         rlist->addItem(new ToolItem("STAxe", 0));
3739                                         found = true;
3740                                 }
3741                         }
3742
3743                         // Steel axe
3744                         if(!found)
3745                         {
3746                                 ItemSpec specs[9];
3747                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3748                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3749                                 specs[3] = 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("SteelAxe", 0));
3755                                         found = true;
3756                                 }
3757                         }
3758
3759                         // Wooden sword
3760                         if(!found)
3761                         {
3762                                 ItemSpec specs[9];
3763                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3764                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3765                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3766                                 if(checkItemCombination(items, specs))
3767                                 {
3768                                         rlist->addItem(new ToolItem("WSword", 0));
3769                                         found = true;
3770                                 }
3771                         }
3772
3773                         // Stone sword
3774                         if(!found)
3775                         {
3776                                 ItemSpec specs[9];
3777                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3778                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3779                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3780                                 if(checkItemCombination(items, specs))
3781                                 {
3782                                         rlist->addItem(new ToolItem("STSword", 0));
3783                                         found = true;
3784                                 }
3785                         }
3786
3787                         // Steel sword
3788                         if(!found)
3789                         {
3790                                 ItemSpec specs[9];
3791                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3792                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3793                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3794                                 if(checkItemCombination(items, specs))
3795                                 {
3796                                         rlist->addItem(new ToolItem("SteelSword", 0));
3797                                         found = true;
3798                                 }
3799                         }
3800
3801                         // Chest
3802                         if(!found)
3803                         {
3804                                 ItemSpec specs[9];
3805                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3806                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3807                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3808                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3809                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3810                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3811                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3812                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3813                                 if(checkItemCombination(items, specs))
3814                                 {
3815                                         rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3816                                         found = true;
3817                                 }
3818                         }
3819
3820                         // Furnace
3821                         if(!found)
3822                         {
3823                                 ItemSpec specs[9];
3824                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3825                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3826                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3827                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3828                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3829                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3830                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3831                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3832                                 if(checkItemCombination(items, specs))
3833                                 {
3834                                         rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3835                                         found = true;
3836                                 }
3837                         }
3838
3839                         // Steel block
3840                         if(!found)
3841                         {
3842                                 ItemSpec specs[9];
3843                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3844                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3845                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3846                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3847                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3848                                 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3849                                 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3850                                 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3851                                 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3852                                 if(checkItemCombination(items, specs))
3853                                 {
3854                                         rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3855                                         found = true;
3856                                 }
3857                         }
3858                 }
3859         
3860         } // if creative_mode == false
3861 }
3862
3863 RemoteClient* Server::getClient(u16 peer_id)
3864 {
3865         DSTACK(__FUNCTION_NAME);
3866         //JMutexAutoLock lock(m_con_mutex);
3867         core::map<u16, RemoteClient*>::Node *n;
3868         n = m_clients.find(peer_id);
3869         // A client should exist for all peers
3870         assert(n != NULL);
3871         return n->getValue();
3872 }
3873
3874 std::wstring Server::getStatusString()
3875 {
3876         std::wostringstream os(std::ios_base::binary);
3877         os<<L"# Server: ";
3878         // Version
3879         os<<L"version="<<narrow_to_wide(VERSION_STRING);
3880         // Uptime
3881         os<<L", uptime="<<m_uptime.get();
3882         // Information about clients
3883         os<<L", clients={";
3884         for(core::map<u16, RemoteClient*>::Iterator
3885                 i = m_clients.getIterator();
3886                 i.atEnd() == false; i++)
3887         {
3888                 // Get client and check that it is valid
3889                 RemoteClient *client = i.getNode()->getValue();
3890                 assert(client->peer_id == i.getNode()->getKey());
3891                 if(client->serialization_version == SER_FMT_VER_INVALID)
3892                         continue;
3893                 // Get player
3894                 Player *player = m_env.getPlayer(client->peer_id);
3895                 // Get name of player
3896                 std::wstring name = L"unknown";
3897                 if(player != NULL)
3898                         name = narrow_to_wide(player->getName());
3899                 // Add name to information string
3900                 os<<name<<L",";
3901         }
3902         os<<L"}";
3903         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3904                 os<<" WARNING: Map saving is disabled."<<std::endl;
3905         return os.str();
3906 }
3907
3908
3909 void setCreativeInventory(Player *player)
3910 {
3911         player->resetInventory();
3912         
3913         // Give some good tools
3914         {
3915                 InventoryItem *item = new ToolItem("MesePick", 0);
3916                 void* r = player->inventory.addItem("main", item);
3917                 assert(r == NULL);
3918         }
3919         {
3920                 InventoryItem *item = new ToolItem("SteelPick", 0);
3921                 void* r = player->inventory.addItem("main", item);
3922                 assert(r == NULL);
3923         }
3924         {
3925                 InventoryItem *item = new ToolItem("SteelAxe", 0);
3926                 void* r = player->inventory.addItem("main", item);
3927                 assert(r == NULL);
3928         }
3929         {
3930                 InventoryItem *item = new ToolItem("SteelShovel", 0);
3931                 void* r = player->inventory.addItem("main", item);
3932                 assert(r == NULL);
3933         }
3934
3935         /*
3936                 Give materials
3937         */
3938         
3939         // CONTENT_IGNORE-terminated list
3940         u8 material_items[] = {
3941                 CONTENT_TORCH,
3942                 CONTENT_COBBLE,
3943                 CONTENT_MUD,
3944                 CONTENT_STONE,
3945                 CONTENT_SAND,
3946                 CONTENT_TREE,
3947                 CONTENT_LEAVES,
3948                 CONTENT_GLASS,
3949                 CONTENT_MESE,
3950                 CONTENT_WATERSOURCE,
3951                 CONTENT_CLOUD,
3952                 CONTENT_CHEST,
3953                 CONTENT_FURNACE,
3954                 CONTENT_SIGN_WALL,
3955                 CONTENT_IGNORE
3956         };
3957         
3958         u8 *mip = material_items;
3959         for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3960         {
3961                 if(*mip == CONTENT_IGNORE)
3962                         break;
3963
3964                 InventoryItem *item = new MaterialItem(*mip, 1);
3965                 player->inventory.addItem("main", item);
3966
3967                 mip++;
3968         }
3969
3970 #if 0
3971         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3972         
3973         // add torch first
3974         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3975         player->inventory.addItem("main", item);
3976         
3977         // Then others
3978         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3979         {
3980                 // Skip some materials
3981                 if(i == CONTENT_WATER || i == CONTENT_TORCH
3982                         || i == CONTENT_COALSTONE)
3983                         continue;
3984
3985                 InventoryItem *item = new MaterialItem(i, 1);
3986                 player->inventory.addItem("main", item);
3987         }
3988 #endif
3989
3990         /*// Sign
3991         {
3992                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3993                 void* r = player->inventory.addItem("main", item);
3994                 assert(r == NULL);
3995         }*/
3996 }
3997
3998 v3f findSpawnPos(ServerMap &map)
3999 {
4000         //return v3f(50,50,50)*BS;
4001         
4002         v2s16 nodepos;
4003         s16 groundheight = 0;
4004         
4005         // Try to find a good place a few times
4006         for(s32 i=0; i<1000; i++)
4007         {
4008                 s32 range = 1 + i;
4009                 // We're going to try to throw the player to this position
4010                 nodepos = v2s16(-range + (myrand()%(range*2)),
4011                                 -range + (myrand()%(range*2)));
4012                 v2s16 sectorpos = getNodeSectorPos(nodepos);
4013                 // Get sector (NOTE: Don't get because it's slow)
4014                 //m_env.getMap().emergeSector(sectorpos);
4015                 // Get ground height at point (fallbacks to heightmap function)
4016                 groundheight = map.findGroundLevel(nodepos);
4017                 // Don't go underwater
4018                 if(groundheight < WATER_LEVEL)
4019                 {
4020                         //dstream<<"-> Underwater"<<std::endl;
4021                         continue;
4022                 }
4023                 // Don't go to high places
4024                 if(groundheight > WATER_LEVEL + 4)
4025                 {
4026                         //dstream<<"-> Underwater"<<std::endl;
4027                         continue;
4028                 }
4029
4030                 // Found a good place
4031                 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4032                 break;
4033         }
4034         
4035         // If no suitable place was not found, go above water at least.
4036         if(groundheight < WATER_LEVEL)
4037                 groundheight = WATER_LEVEL;
4038
4039         return intToFloat(v3s16(
4040                         nodepos.X,
4041                         groundheight + 2,
4042                         nodepos.Y
4043                         ), BS);
4044 }
4045
4046 Player *Server::emergePlayer(const char *name, const char *password,
4047                 u16 peer_id)
4048 {
4049         /*
4050                 Try to get an existing player
4051         */
4052         Player *player = m_env.getPlayer(name);
4053         if(player != NULL)
4054         {
4055                 // If player is already connected, cancel
4056                 if(player->peer_id != 0)
4057                 {
4058                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
4059                         return NULL;
4060                 }
4061
4062                 // Got one.
4063                 player->peer_id = peer_id;
4064                 
4065                 // Reset inventory to creative if in creative mode
4066                 if(g_settings.getBool("creative_mode"))
4067                 {
4068                         setCreativeInventory(player);
4069                 }
4070
4071                 return player;
4072         }
4073
4074         /*
4075                 If player with the wanted peer_id already exists, cancel.
4076         */
4077         if(m_env.getPlayer(peer_id) != NULL)
4078         {
4079                 dstream<<"emergePlayer(): Player with wrong name but same"
4080                                 " peer_id already exists"<<std::endl;
4081                 return NULL;
4082         }
4083         
4084         /*
4085                 Create a new player
4086         */
4087         {
4088                 player = new ServerRemotePlayer();
4089                 //player->peer_id = c.peer_id;
4090                 //player->peer_id = PEER_ID_INEXISTENT;
4091                 player->peer_id = peer_id;
4092                 player->updateName(name);
4093
4094                 /*
4095                         Set player position
4096                 */
4097                 
4098                 dstream<<"Server: Finding spawn place for player \""
4099                                 <<player->getName()<<"\""<<std::endl;
4100
4101                 v3f pos = findSpawnPos(m_env.getServerMap());
4102
4103                 player->setPosition(pos);
4104
4105                 /*
4106                         Add player to environment
4107                 */
4108
4109                 m_env.addPlayer(player);
4110
4111                 /*
4112                         Add stuff to inventory
4113                 */
4114                 
4115                 if(g_settings.getBool("creative_mode"))
4116                 {
4117                         setCreativeInventory(player);
4118                 }
4119                 else if(g_settings.getBool("give_initial_stuff"))
4120                 {
4121                         {
4122                                 InventoryItem *item = new ToolItem("SteelPick", 0);
4123                                 void* r = player->inventory.addItem("main", item);
4124                                 assert(r == NULL);
4125                         }
4126                         {
4127                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4128                                 void* r = player->inventory.addItem("main", item);
4129                                 assert(r == NULL);
4130                         }
4131                         {
4132                                 InventoryItem *item = new ToolItem("SteelAxe", 0);
4133                                 void* r = player->inventory.addItem("main", item);
4134                                 assert(r == NULL);
4135                         }
4136                         {
4137                                 InventoryItem *item = new ToolItem("SteelShovel", 0);
4138                                 void* r = player->inventory.addItem("main", item);
4139                                 assert(r == NULL);
4140                         }
4141                         {
4142                                 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4143                                 void* r = player->inventory.addItem("main", item);
4144                                 assert(r == NULL);
4145                         }
4146                         /*{
4147                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4148                                 void* r = player->inventory.addItem("main", item);
4149                                 assert(r == NULL);
4150                         }
4151                         {
4152                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4153                                 void* r = player->inventory.addItem("main", item);
4154                                 assert(r == NULL);
4155                         }
4156                         {
4157                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4158                                 void* r = player->inventory.addItem("main", item);
4159                                 assert(r == NULL);
4160                         }
4161                         {
4162                                 InventoryItem *item = new CraftItem("Stick", 4);
4163                                 void* r = player->inventory.addItem("main", item);
4164                                 assert(r == NULL);
4165                         }
4166                         {
4167                                 InventoryItem *item = new ToolItem("WPick", 32000);
4168                                 void* r = player->inventory.addItem("main", item);
4169                                 assert(r == NULL);
4170                         }
4171                         {
4172                                 InventoryItem *item = new ToolItem("STPick", 32000);
4173                                 void* r = player->inventory.addItem("main", item);
4174                                 assert(r == NULL);
4175                         }*/
4176                         /*// and some signs
4177                         for(u16 i=0; i<4; i++)
4178                         {
4179                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4180                                 bool r = player->inventory.addItem("main", item);
4181                                 assert(r == true);
4182                         }*/
4183                         /*// Give some other stuff
4184                         {
4185                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4186                                 bool r = player->inventory.addItem("main", item);
4187                                 assert(r == true);
4188                         }*/
4189                 }
4190
4191                 return player;
4192                 
4193         } // create new player
4194 }
4195
4196 void Server::handlePeerChange(PeerChange &c)
4197 {
4198         JMutexAutoLock envlock(m_env_mutex);
4199         JMutexAutoLock conlock(m_con_mutex);
4200         
4201         if(c.type == PEER_ADDED)
4202         {
4203                 /*
4204                         Add
4205                 */
4206
4207                 // Error check
4208                 core::map<u16, RemoteClient*>::Node *n;
4209                 n = m_clients.find(c.peer_id);
4210                 // The client shouldn't already exist
4211                 assert(n == NULL);
4212
4213                 // Create client
4214                 RemoteClient *client = new RemoteClient();
4215                 client->peer_id = c.peer_id;
4216                 m_clients.insert(client->peer_id, client);
4217
4218         } // PEER_ADDED
4219         else if(c.type == PEER_REMOVED)
4220         {
4221                 /*
4222                         Delete
4223                 */
4224
4225                 // Error check
4226                 core::map<u16, RemoteClient*>::Node *n;
4227                 n = m_clients.find(c.peer_id);
4228                 // The client should exist
4229                 assert(n != NULL);
4230                 
4231                 /*
4232                         Mark objects to be not known by the client
4233                 */
4234                 RemoteClient *client = n->getValue();
4235                 // Handle objects
4236                 for(core::map<u16, bool>::Iterator
4237                                 i = client->m_known_objects.getIterator();
4238                                 i.atEnd()==false; i++)
4239                 {
4240                         // Get object
4241                         u16 id = i.getNode()->getKey();
4242                         ServerActiveObject* obj = m_env.getActiveObject(id);
4243                         
4244                         if(obj && obj->m_known_by_count > 0)
4245                                 obj->m_known_by_count--;
4246                 }
4247
4248                 // Collect information about leaving in chat
4249                 std::wstring message;
4250                 {
4251                         std::wstring name = L"unknown";
4252                         Player *player = m_env.getPlayer(c.peer_id);
4253                         if(player != NULL)
4254                                 name = narrow_to_wide(player->getName());
4255                         
4256                         message += L"*** ";
4257                         message += name;
4258                         message += L" left game";
4259                         if(c.timeout)
4260                                 message += L" (timed out)";
4261                 }
4262
4263                 /*// Delete player
4264                 {
4265                         m_env.removePlayer(c.peer_id);
4266                 }*/
4267
4268                 // Set player client disconnected
4269                 {
4270                         Player *player = m_env.getPlayer(c.peer_id);
4271                         if(player != NULL)
4272                                 player->peer_id = 0;
4273                 }
4274                 
4275                 // Delete client
4276                 delete m_clients[c.peer_id];
4277                 m_clients.remove(c.peer_id);
4278
4279                 // Send player info to all remaining clients
4280                 SendPlayerInfos();
4281                 
4282                 // Send leave chat message to all remaining clients
4283                 BroadcastChatMessage(message);
4284                 
4285         } // PEER_REMOVED
4286         else
4287         {
4288                 assert(0);
4289         }
4290 }
4291
4292 void Server::handlePeerChanges()
4293 {
4294         while(m_peer_change_queue.size() > 0)
4295         {
4296                 PeerChange c = m_peer_change_queue.pop_front();
4297
4298                 dout_server<<"Server: Handling peer change: "
4299                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4300                                 <<std::endl;
4301
4302                 handlePeerChange(c);
4303         }
4304 }
4305
4306 void dedicated_server_loop(Server &server, bool &kill)
4307 {
4308         DSTACK(__FUNCTION_NAME);
4309         
4310         std::cout<<DTIME<<std::endl;
4311         std::cout<<"========================"<<std::endl;
4312         std::cout<<"Running dedicated server"<<std::endl;
4313         std::cout<<"========================"<<std::endl;
4314         std::cout<<std::endl;
4315
4316         for(;;)
4317         {
4318                 // This is kind of a hack but can be done like this
4319                 // because server.step() is very light
4320                 sleep_ms(30);
4321                 server.step(0.030);
4322
4323                 if(server.getShutdownRequested() || kill)
4324                 {
4325                         std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4326                         break;
4327                 }
4328
4329                 static int counter = 0;
4330                 counter--;
4331                 if(counter <= 0)
4332                 {
4333                         counter = 10;
4334
4335                         core::list<PlayerInfo> list = server.getPlayerInfo();
4336                         core::list<PlayerInfo>::Iterator i;
4337                         static u32 sum_old = 0;
4338                         u32 sum = PIChecksum(list);
4339                         if(sum != sum_old)
4340                         {
4341                                 std::cout<<DTIME<<"Player info:"<<std::endl;
4342                                 for(i=list.begin(); i!=list.end(); i++)
4343                                 {
4344                                         i->PrintLine(&std::cout);
4345                                 }
4346                         }
4347                         sum_old = sum;
4348                 }
4349         }
4350 }
4351
4352