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