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