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