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