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