]> git.lizzy.rs Git - dragonfireclient.git/blob - src/server.cpp
Merge pull request #48 from Oblomov/master
[dragonfireclient.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "server.h"
21 #include "utility.h"
22 #include <iostream>
23 #include "clientserver.h"
24 #include "map.h"
25 #include "jmutexautolock.h"
26 #include "main.h"
27 #include "constants.h"
28 #include "voxel.h"
29 #include "materials.h"
30 #include "mineral.h"
31 #include "config.h"
32 #include "servercommand.h"
33 #include "filesys.h"
34 #include "content_mapnode.h"
35 #include "content_craft.h"
36 #include "content_nodemeta.h"
37 #include "mapblock.h"
38 #include "serverobject.h"
39
40 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
41
42 class MapEditEventIgnorer
43 {
44 public:
45         MapEditEventIgnorer(bool *flag):
46                 m_flag(flag)
47         {
48                 if(*m_flag == false)
49                         *m_flag = true;
50                 else
51                         m_flag = NULL;
52         }
53
54         ~MapEditEventIgnorer()
55         {
56                 if(m_flag)
57                 {
58                         assert(*m_flag);
59                         *m_flag = false;
60                 }
61         }
62         
63 private:
64         bool *m_flag;
65 };
66
67 void * ServerThread::Thread()
68 {
69         ThreadStarted();
70
71         DSTACK(__FUNCTION_NAME);
72
73         BEGIN_DEBUG_EXCEPTION_HANDLER
74
75         while(getRun())
76         {
77                 try{
78                         //TimeTaker timer("AsyncRunStep() + Receive()");
79
80                         {
81                                 //TimeTaker timer("AsyncRunStep()");
82                                 m_server->AsyncRunStep();
83                         }
84                 
85                         //dout_server<<"Running m_server->Receive()"<<std::endl;
86                         m_server->Receive();
87                 }
88                 catch(con::NoIncomingDataException &e)
89                 {
90                 }
91                 catch(con::PeerNotFoundException &e)
92                 {
93                         dout_server<<"Server: PeerNotFoundException"<<std::endl;
94                 }
95         }
96         
97         END_DEBUG_EXCEPTION_HANDLER
98
99         return NULL;
100 }
101
102 void * EmergeThread::Thread()
103 {
104         ThreadStarted();
105
106         DSTACK(__FUNCTION_NAME);
107
108         BEGIN_DEBUG_EXCEPTION_HANDLER
109
110         bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
111         
112         /*
113                 Get block info from queue, emerge them and send them
114                 to clients.
115
116                 After queue is empty, exit.
117         */
118         while(getRun())
119         {
120                 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
121                 if(qptr == NULL)
122                         break;
123                 
124                 SharedPtr<QueuedBlockEmerge> q(qptr);
125
126                 v3s16 &p = q->pos;
127                 v2s16 p2d(p.X,p.Z);
128
129                 /*
130                         Do not generate over-limit
131                 */
132                 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
133                 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
134                 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
135                 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
136                 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
137                 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
138                         continue;
139                         
140                 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
141
142                 //TimeTaker timer("block emerge");
143                 
144                 /*
145                         Try to emerge it from somewhere.
146
147                         If it is only wanted as optional, only loading from disk
148                         will be allowed.
149                 */
150                 
151                 /*
152                         Check if any peer wants it as non-optional. In that case it
153                         will be generated.
154
155                         Also decrement the emerge queue count in clients.
156                 */
157
158                 bool only_from_disk = true;
159
160                 {
161                         core::map<u16, u8>::Iterator i;
162                         for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
163                         {
164                                 //u16 peer_id = i.getNode()->getKey();
165
166                                 // Check flags
167                                 u8 flags = i.getNode()->getValue();
168                                 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
169                                         only_from_disk = false;
170                                 
171                         }
172                 }
173                 
174                 if(enable_mapgen_debug_info)
175                         dstream<<"EmergeThread: p="
176                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
177                                         <<"only_from_disk="<<only_from_disk<<std::endl;
178                 
179                 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
180                         
181                 //core::map<v3s16, MapBlock*> changed_blocks;
182                 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
183
184                 MapBlock *block = NULL;
185                 bool got_block = true;
186                 core::map<v3s16, MapBlock*> modified_blocks;
187                 
188                 /*
189                         Fetch block from map or generate a single block
190                 */
191                 {
192                         JMutexAutoLock envlock(m_server->m_env_mutex);
193                         
194                         // Load sector if it isn't loaded
195                         if(map.getSectorNoGenerateNoEx(p2d) == NULL)
196                                 //map.loadSectorFull(p2d);
197                                 map.loadSectorMeta(p2d);
198
199                         block = map.getBlockNoCreateNoEx(p);
200                         if(!block || block->isDummy() || !block->isGenerated())
201                         {
202                                 if(enable_mapgen_debug_info)
203                                         dstream<<"EmergeThread: not in memory, loading"<<std::endl;
204
205                                 // Get, load or create sector
206                                 /*ServerMapSector *sector =
207                                                 (ServerMapSector*)map.createSector(p2d);*/
208
209                                 // Load/generate block
210
211                                 /*block = map.emergeBlock(p, sector, changed_blocks,
212                                                 lighting_invalidated_blocks);*/
213
214                                 block = map.loadBlock(p);
215                                 
216                                 if(only_from_disk == false)
217                                 {
218                                         if(block == NULL || block->isGenerated() == false)
219                                         {
220                                                 if(enable_mapgen_debug_info)
221                                                         dstream<<"EmergeThread: generating"<<std::endl;
222                                                 block = map.generateBlock(p, modified_blocks);
223                                         }
224                                 }
225
226                                 if(enable_mapgen_debug_info)
227                                         dstream<<"EmergeThread: ended up with: "
228                                                         <<analyze_block(block)<<std::endl;
229
230                                 if(block == NULL)
231                                 {
232                                         got_block = false;
233                                 }
234                                 else
235                                 {
236                                         /*
237                                                 Ignore map edit events, they will not need to be
238                                                 sent to anybody because the block hasn't been sent
239                                                 to anybody
240                                         */
241                                         MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
242                                         
243                                         // Activate objects and stuff
244                                         m_server->m_env.activateBlock(block, 3600);
245                                 }
246                         }
247                         else
248                         {
249                                 /*if(block->getLightingExpired()){
250                                         lighting_invalidated_blocks[block->getPos()] = block;
251                                 }*/
252                         }
253
254                         // TODO: Some additional checking and lighting updating,
255                         //       see emergeBlock
256                 }
257
258                 {//envlock
259                 JMutexAutoLock envlock(m_server->m_env_mutex);
260                 
261                 if(got_block)
262                 {
263                         /*
264                                 Collect a list of blocks that have been modified in
265                                 addition to the fetched one.
266                         */
267
268 #if 0
269                         if(lighting_invalidated_blocks.size() > 0)
270                         {
271                                 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
272                                                 <<" blocks"<<std::endl;*/
273                         
274                                 // 50-100ms for single block generation
275                                 //TimeTaker timer("** EmergeThread updateLighting");
276                                 
277                                 // Update lighting without locking the environment mutex,
278                                 // add modified blocks to changed blocks
279                                 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
280                         }
281                                 
282                         // Add all from changed_blocks to modified_blocks
283                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
284                                         i.atEnd() == false; i++)
285                         {
286                                 MapBlock *block = i.getNode()->getValue();
287                                 modified_blocks.insert(block->getPos(), block);
288                         }
289 #endif
290                 }
291                 // If we got no block, there should be no invalidated blocks
292                 else
293                 {
294                         //assert(lighting_invalidated_blocks.size() == 0);
295                 }
296
297                 }//envlock
298
299                 /*
300                         Set sent status of modified blocks on clients
301                 */
302         
303                 // NOTE: Server's clients are also behind the connection mutex
304                 JMutexAutoLock lock(m_server->m_con_mutex);
305
306                 /*
307                         Add the originally fetched block to the modified list
308                 */
309                 if(got_block)
310                 {
311                         modified_blocks.insert(p, block);
312                 }
313                 
314                 /*
315                         Set the modified blocks unsent for all the clients
316                 */
317                 
318                 for(core::map<u16, RemoteClient*>::Iterator
319                                 i = m_server->m_clients.getIterator();
320                                 i.atEnd() == false; i++)
321                 {
322                         RemoteClient *client = i.getNode()->getValue();
323                         
324                         if(modified_blocks.size() > 0)
325                         {
326                                 // Remove block from sent history
327                                 client->SetBlocksNotSent(modified_blocks);
328                         }
329                 }
330                 
331         }
332
333         END_DEBUG_EXCEPTION_HANDLER
334
335         return NULL;
336 }
337
338 void RemoteClient::GetNextBlocks(Server *server, float dtime,
339                 core::array<PrioritySortedBlockTransfer> &dest)
340 {
341         DSTACK(__FUNCTION_NAME);
342         
343         /*u32 timer_result;
344         TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
345         
346         // Increment timers
347         m_nothing_to_send_pause_timer -= dtime;
348         
349         if(m_nothing_to_send_pause_timer >= 0)
350         {
351                 // Keep this reset
352                 m_nearest_unsent_reset_timer = 0;
353                 return;
354         }
355
356         // Won't send anything if already sending
357         if(m_blocks_sending.size() >= g_settings.getU16
358                         ("max_simultaneous_block_sends_per_client"))
359         {
360                 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
361                 return;
362         }
363
364         //TimeTaker timer("RemoteClient::GetNextBlocks");
365         
366         Player *player = server->m_env.getPlayer(peer_id);
367
368         assert(player != NULL);
369
370         v3f playerpos = player->getPosition();
371         v3f playerspeed = player->getSpeed();
372         v3f playerspeeddir(0,0,0);
373         if(playerspeed.getLength() > 1.0*BS)
374                 playerspeeddir = playerspeed / playerspeed.getLength();
375         // Predict to next block
376         v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
377
378         v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
379
380         v3s16 center = getNodeBlockPos(center_nodepos);
381         
382         // Camera position and direction
383         v3f camera_pos =
384                         playerpos + v3f(0, BS+BS/2, 0);
385         v3f camera_dir = v3f(0,0,1);
386         camera_dir.rotateYZBy(player->getPitch());
387         camera_dir.rotateXZBy(player->getYaw());
388
389         /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
390                         <<camera_dir.Z<<")"<<std::endl;*/
391
392         /*
393                 Get the starting value of the block finder radius.
394         */
395                 
396         if(m_last_center != center)
397         {
398                 m_nearest_unsent_d = 0;
399                 m_last_center = center;
400         }
401
402         /*dstream<<"m_nearest_unsent_reset_timer="
403                         <<m_nearest_unsent_reset_timer<<std::endl;*/
404                         
405         // This has to be incremented only when the nothing to send pause
406         // is not active
407         m_nearest_unsent_reset_timer += dtime;
408         
409         // Reset periodically to avoid possible bugs or other mishaps
410         if(m_nearest_unsent_reset_timer > 10.0)
411         {
412                 m_nearest_unsent_reset_timer = 0;
413                 m_nearest_unsent_d = 0;
414                 /*dstream<<"Resetting m_nearest_unsent_d for "
415                                 <<server->getPlayerName(peer_id)<<std::endl;*/
416         }
417
418         //s16 last_nearest_unsent_d = m_nearest_unsent_d;
419         s16 d_start = m_nearest_unsent_d;
420
421         //dstream<<"d_start="<<d_start<<std::endl;
422
423         u16 max_simul_sends_setting = g_settings.getU16
424                         ("max_simultaneous_block_sends_per_client");
425         u16 max_simul_sends_usually = max_simul_sends_setting;
426
427         /*
428                 Check the time from last addNode/removeNode.
429                 
430                 Decrease send rate if player is building stuff.
431         */
432         m_time_from_building += dtime;
433         if(m_time_from_building < g_settings.getFloat(
434                                 "full_block_send_enable_min_time_from_building"))
435         {
436                 max_simul_sends_usually
437                         = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
438         }
439         
440         /*
441                 Number of blocks sending + number of blocks selected for sending
442         */
443         u32 num_blocks_selected = m_blocks_sending.size();
444         
445         /*
446                 next time d will be continued from the d from which the nearest
447                 unsent block was found this time.
448
449                 This is because not necessarily any of the blocks found this
450                 time are actually sent.
451         */
452         s32 new_nearest_unsent_d = -1;
453
454         s16 d_max = g_settings.getS16("max_block_send_distance");
455         s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
456         
457         // Don't loop very much at a time
458         if(d_max > d_start+1)
459                 d_max = d_start+1;
460         /*if(d_max_gen > d_start+2)
461                 d_max_gen = d_start+2;*/
462         
463         //dstream<<"Starting from "<<d_start<<std::endl;
464
465         bool sending_something = false;
466
467         bool no_blocks_found_for_sending = true;
468
469         bool queue_is_full = false;
470         
471         s16 d;
472         for(d = d_start; d <= d_max; d++)
473         {
474                 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
475                 
476                 /*
477                         If m_nearest_unsent_d was changed by the EmergeThread
478                         (it can change it to 0 through SetBlockNotSent),
479                         update our d to it.
480                         Else update m_nearest_unsent_d
481                 */
482                 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
483                 {
484                         d = m_nearest_unsent_d;
485                         last_nearest_unsent_d = m_nearest_unsent_d;
486                 }*/
487
488                 /*
489                         Get the border/face dot coordinates of a "d-radiused"
490                         box
491                 */
492                 core::list<v3s16> list;
493                 getFacePositions(list, d);
494                 
495                 core::list<v3s16>::Iterator li;
496                 for(li=list.begin(); li!=list.end(); li++)
497                 {
498                         v3s16 p = *li + center;
499                         
500                         /*
501                                 Send throttling
502                                 - Don't allow too many simultaneous transfers
503                                 - EXCEPT when the blocks are very close
504
505                                 Also, don't send blocks that are already flying.
506                         */
507                         
508                         // Start with the usual maximum
509                         u16 max_simul_dynamic = max_simul_sends_usually;
510                         
511                         // If block is very close, allow full maximum
512                         if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
513                                 max_simul_dynamic = max_simul_sends_setting;
514
515                         // Don't select too many blocks for sending
516                         if(num_blocks_selected >= max_simul_dynamic)
517                         {
518                                 queue_is_full = true;
519                                 goto queue_full_break;
520                         }
521                         
522                         // Don't send blocks that are currently being transferred
523                         if(m_blocks_sending.find(p) != NULL)
524                                 continue;
525                 
526                         /*
527                                 Do not go over-limit
528                         */
529                         if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
530                         || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
531                         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
532                         || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
533                         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
534                         || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
535                                 continue;
536                 
537                         // If this is true, inexistent block will be made from scratch
538                         bool generate = d <= d_max_gen;
539                         
540                         {
541                                 /*// Limit the generating area vertically to 2/3
542                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
543                                         generate = false;*/
544
545                                 // Limit the send area vertically to 2/3
546                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
547                                         continue;
548                         }
549
550 #if 1
551                         /*
552                                 If block is far away, don't generate it unless it is
553                                 near ground level.
554                         */
555                         if(d >= 4)
556                         {
557         #if 1
558                                 // Block center y in nodes
559                                 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
560                                 // Don't generate if it's very high or very low
561                                 if(y < -64 || y > 64)
562                                         generate = false;
563         #endif
564         #if 0
565                                 v2s16 p2d_nodes_center(
566                                         MAP_BLOCKSIZE*p.X,
567                                         MAP_BLOCKSIZE*p.Z);
568                                 
569                                 // Get ground height in nodes
570                                 s16 gh = server->m_env.getServerMap().findGroundLevel(
571                                                 p2d_nodes_center);
572
573                                 // If differs a lot, don't generate
574                                 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
575                                         generate = false;
576                                         // Actually, don't even send it
577                                         //continue;
578         #endif
579                         }
580 #endif
581
582                         //dstream<<"d="<<d<<std::endl;
583                         
584                         /*
585                                 Don't generate or send if not in sight
586                         */
587
588                         if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
589                         {
590                                 continue;
591                         }
592                         
593                         /*
594                                 Don't send already sent blocks
595                         */
596                         {
597                                 if(m_blocks_sent.find(p) != NULL)
598                                 {
599                                         continue;
600                                 }
601                         }
602
603                         /*
604                                 Check if map has this block
605                         */
606                         MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
607                         
608                         bool surely_not_found_on_disk = false;
609                         bool block_is_invalid = false;
610                         if(block != NULL)
611                         {
612                                 // Reset usage timer, this block will be of use in the future.
613                                 block->resetUsageTimer();
614
615                                 // Block is dummy if data doesn't exist.
616                                 // It means it has been not found from disk and not generated
617                                 if(block->isDummy())
618                                 {
619                                         surely_not_found_on_disk = true;
620                                 }
621                                 
622                                 // Block is valid if lighting is up-to-date and data exists
623                                 if(block->isValid() == false)
624                                 {
625                                         block_is_invalid = true;
626                                 }
627                                 
628                                 /*if(block->isFullyGenerated() == false)
629                                 {
630                                         block_is_invalid = true;
631                                 }*/
632
633 #if 0
634                                 v2s16 p2d(p.X, p.Z);
635                                 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
636                                 v2s16 chunkpos = map->sector_to_chunk(p2d);
637                                 if(map->chunkNonVolatile(chunkpos) == false)
638                                         block_is_invalid = true;
639 #endif
640                                 if(block->isGenerated() == false)
641                                         block_is_invalid = true;
642 #if 1
643                                 /*
644                                         If block is not close, don't send it unless it is near
645                                         ground level.
646
647                                         Block is near ground level if night-time mesh
648                                         differs from day-time mesh.
649                                 */
650                                 if(d > 3)
651                                 {
652                                         if(block->dayNightDiffed() == false)
653                                                 continue;
654                                 }
655 #endif
656                         }
657
658                         /*
659                                 If block has been marked to not exist on disk (dummy)
660                                 and generating new ones is not wanted, skip block.
661                         */
662                         if(generate == false && surely_not_found_on_disk == true)
663                         {
664                                 // get next one.
665                                 continue;
666                         }
667
668                         /*
669                                 Record the lowest d from which a block has been
670                                 found being not sent and possibly to exist
671                         */
672                         if(no_blocks_found_for_sending)
673                         {
674                                 if(generate == true)
675                                         new_nearest_unsent_d = d;
676                         }
677
678                         no_blocks_found_for_sending = false;
679                                         
680                         /*
681                                 Add inexistent block to emerge queue.
682                         */
683                         if(block == NULL || surely_not_found_on_disk || block_is_invalid)
684                         {
685                                 //TODO: Get value from somewhere
686                                 // Allow only one block in emerge queue
687                                 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
688                                 // Allow two blocks in queue per client
689                                 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
690                                 {
691                                         //dstream<<"Adding block to emerge queue"<<std::endl;
692                                         
693                                         // Add it to the emerge queue and trigger the thread
694                                         
695                                         u8 flags = 0;
696                                         if(generate == false)
697                                                 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
698                                         
699                                         server->m_emerge_queue.addBlock(peer_id, p, flags);
700                                         server->m_emergethread.trigger();
701                                 }
702                                 
703                                 // get next one.
704                                 continue;
705                         }
706
707                         /*
708                                 Add block to send queue
709                         */
710
711                         PrioritySortedBlockTransfer q((float)d, p, peer_id);
712
713                         dest.push_back(q);
714
715                         num_blocks_selected += 1;
716                         sending_something = true;
717                 }
718         }
719 queue_full_break:
720
721         //dstream<<"Stopped at "<<d<<std::endl;
722         
723         if(no_blocks_found_for_sending)
724         {
725                 if(queue_is_full == false)
726                         new_nearest_unsent_d = d;
727         }
728
729         if(new_nearest_unsent_d != -1)
730                 m_nearest_unsent_d = new_nearest_unsent_d;
731
732         if(sending_something == false)
733         {
734                 m_nothing_to_send_counter++;
735                 if((s16)m_nothing_to_send_counter >=
736                                 g_settings.getS16("max_block_send_distance"))
737                 {
738                         // Pause time in seconds
739                         m_nothing_to_send_pause_timer = 1.0;
740                         /*dstream<<"nothing to send to "
741                                         <<server->getPlayerName(peer_id)
742                                         <<" (d="<<d<<")"<<std::endl;*/
743                 }
744         }
745         else
746         {
747                 m_nothing_to_send_counter = 0;
748         }
749
750         /*timer_result = timer.stop(true);
751         if(timer_result != 0)
752                 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
753 }
754
755 void RemoteClient::SendObjectData(
756                 Server *server,
757                 float dtime,
758                 core::map<v3s16, bool> &stepped_blocks
759         )
760 {
761         DSTACK(__FUNCTION_NAME);
762
763         // Can't send anything without knowing version
764         if(serialization_version == SER_FMT_VER_INVALID)
765         {
766                 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
767                                 <<std::endl;
768                 return;
769         }
770
771         /*
772                 Send a TOCLIENT_OBJECTDATA packet.
773                 Sent as unreliable.
774
775                 u16 command
776                 u16 number of player positions
777                 for each player:
778                         v3s32 position*100
779                         v3s32 speed*100
780                         s32 pitch*100
781                         s32 yaw*100
782                 u16 count of blocks
783                 for each block:
784                         block objects
785         */
786
787         std::ostringstream os(std::ios_base::binary);
788         u8 buf[12];
789         
790         // Write command
791         writeU16(buf, TOCLIENT_OBJECTDATA);
792         os.write((char*)buf, 2);
793         
794         /*
795                 Get and write player data
796         */
797         
798         // Get connected players
799         core::list<Player*> players = server->m_env.getPlayers(true);
800
801         // Write player count
802         u16 playercount = players.size();
803         writeU16(buf, playercount);
804         os.write((char*)buf, 2);
805
806         core::list<Player*>::Iterator i;
807         for(i = players.begin();
808                         i != players.end(); i++)
809         {
810                 Player *player = *i;
811
812                 v3f pf = player->getPosition();
813                 v3f sf = player->getSpeed();
814
815                 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
816                 v3s32 speed_i   (sf.X*100, sf.Y*100, sf.Z*100);
817                 s32   pitch_i   (player->getPitch() * 100);
818                 s32   yaw_i     (player->getYaw() * 100);
819                 
820                 writeU16(buf, player->peer_id);
821                 os.write((char*)buf, 2);
822                 writeV3S32(buf, position_i);
823                 os.write((char*)buf, 12);
824                 writeV3S32(buf, speed_i);
825                 os.write((char*)buf, 12);
826                 writeS32(buf, pitch_i);
827                 os.write((char*)buf, 4);
828                 writeS32(buf, yaw_i);
829                 os.write((char*)buf, 4);
830         }
831         
832         /*
833                 Get and write object data
834         */
835
836         /*
837                 Get nearby blocks.
838                 
839                 For making players to be able to build to their nearby
840                 environment (building is not possible on blocks that are not
841                 in memory):
842                 - Set blocks changed
843                 - Add blocks to emerge queue if they are not found
844
845                 SUGGESTION: These could be ignored from the backside of the player
846         */
847
848         Player *player = server->m_env.getPlayer(peer_id);
849
850         assert(player);
851
852         v3f playerpos = player->getPosition();
853         v3f playerspeed = player->getSpeed();
854
855         v3s16 center_nodepos = floatToInt(playerpos, BS);
856         v3s16 center = getNodeBlockPos(center_nodepos);
857
858         s16 d_max = g_settings.getS16("active_object_range");
859         
860         // Number of blocks whose objects were written to bos
861         u16 blockcount = 0;
862
863         std::ostringstream bos(std::ios_base::binary);
864
865         for(s16 d = 0; d <= d_max; d++)
866         {
867                 core::list<v3s16> list;
868                 getFacePositions(list, d);
869                 
870                 core::list<v3s16>::Iterator li;
871                 for(li=list.begin(); li!=list.end(); li++)
872                 {
873                         v3s16 p = *li + center;
874
875                         /*
876                                 Ignore blocks that haven't been sent to the client
877                         */
878                         {
879                                 if(m_blocks_sent.find(p) == NULL)
880                                         continue;
881                         }
882                         
883                         // Try stepping block and add it to a send queue
884                         try
885                         {
886
887                         // Get block
888                         MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
889
890                         /*
891                                 Step block if not in stepped_blocks and add to stepped_blocks.
892                         */
893                         if(stepped_blocks.find(p) == NULL)
894                         {
895                                 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
896                                 stepped_blocks.insert(p, true);
897                                 block->setChangedFlag();
898                         }
899
900                         // Skip block if there are no objects
901                         if(block->getObjectCount() == 0)
902                                 continue;
903                         
904                         /*
905                                 Write objects
906                         */
907
908                         // Write blockpos
909                         writeV3S16(buf, p);
910                         bos.write((char*)buf, 6);
911
912                         // Write objects
913                         //block->serializeObjects(bos, serialization_version); // DEPRECATED
914                         // count=0
915                         writeU16(bos, 0);
916
917                         blockcount++;
918
919                         /*
920                                 Stop collecting objects if data is already too big
921                         */
922                         // Sum of player and object data sizes
923                         s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
924                         // break out if data too big
925                         if(sum > MAX_OBJECTDATA_SIZE)
926                         {
927                                 goto skip_subsequent;
928                         }
929                         
930                         } //try
931                         catch(InvalidPositionException &e)
932                         {
933                                 // Not in memory
934                                 // Add it to the emerge queue and trigger the thread.
935                                 // Fetch the block only if it is on disk.
936                                 
937                                 // Grab and increment counter
938                                 /*SharedPtr<JMutexAutoLock> lock
939                                                 (m_num_blocks_in_emerge_queue.getLock());
940                                 m_num_blocks_in_emerge_queue.m_value++;*/
941                                 
942                                 // Add to queue as an anonymous fetch from disk
943                                 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
944                                 server->m_emerge_queue.addBlock(0, p, flags);
945                                 server->m_emergethread.trigger();
946                         }
947                 }
948         }
949
950 skip_subsequent:
951
952         // Write block count
953         writeU16(buf, blockcount);
954         os.write((char*)buf, 2);
955
956         // Write block objects
957         os<<bos.str();
958
959         /*
960                 Send data
961         */
962         
963         //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
964
965         // Make data buffer
966         std::string s = os.str();
967         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
968         // Send as unreliable
969         server->m_con.Send(peer_id, 0, data, false);
970 }
971
972 void RemoteClient::GotBlock(v3s16 p)
973 {
974         if(m_blocks_sending.find(p) != NULL)
975                 m_blocks_sending.remove(p);
976         else
977         {
978                 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
979                                 " m_blocks_sending"<<std::endl;*/
980                 m_excess_gotblocks++;
981         }
982         m_blocks_sent.insert(p, true);
983 }
984
985 void RemoteClient::SentBlock(v3s16 p)
986 {
987         if(m_blocks_sending.find(p) == NULL)
988                 m_blocks_sending.insert(p, 0.0);
989         else
990                 dstream<<"RemoteClient::SentBlock(): Sent block"
991                                 " already in m_blocks_sending"<<std::endl;
992 }
993
994 void RemoteClient::SetBlockNotSent(v3s16 p)
995 {
996         m_nearest_unsent_d = 0;
997         
998         if(m_blocks_sending.find(p) != NULL)
999                 m_blocks_sending.remove(p);
1000         if(m_blocks_sent.find(p) != NULL)
1001                 m_blocks_sent.remove(p);
1002 }
1003
1004 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
1005 {
1006         m_nearest_unsent_d = 0;
1007         
1008         for(core::map<v3s16, MapBlock*>::Iterator
1009                         i = blocks.getIterator();
1010                         i.atEnd()==false; i++)
1011         {
1012                 v3s16 p = i.getNode()->getKey();
1013
1014                 if(m_blocks_sending.find(p) != NULL)
1015                         m_blocks_sending.remove(p);
1016                 if(m_blocks_sent.find(p) != NULL)
1017                         m_blocks_sent.remove(p);
1018         }
1019 }
1020
1021 /*
1022         PlayerInfo
1023 */
1024
1025 PlayerInfo::PlayerInfo()
1026 {
1027         name[0] = 0;
1028         avg_rtt = 0;
1029 }
1030
1031 void PlayerInfo::PrintLine(std::ostream *s)
1032 {
1033         (*s)<<id<<": ";
1034         (*s)<<"\""<<name<<"\" ("
1035                         <<(position.X/10)<<","<<(position.Y/10)
1036                         <<","<<(position.Z/10)<<") ";
1037         address.print(s);
1038         (*s)<<" avg_rtt="<<avg_rtt;
1039         (*s)<<std::endl;
1040 }
1041
1042 u32 PIChecksum(core::list<PlayerInfo> &l)
1043 {
1044         core::list<PlayerInfo>::Iterator i;
1045         u32 checksum = 1;
1046         u32 a = 10;
1047         for(i=l.begin(); i!=l.end(); i++)
1048         {
1049                 checksum += a * (i->id+1);
1050                 checksum ^= 0x435aafcd;
1051                 a *= 10;
1052         }
1053         return checksum;
1054 }
1055
1056 /*
1057         Server
1058 */
1059
1060 Server::Server(
1061                 std::string mapsavedir,
1062                 std::string configpath
1063         ):
1064         m_env(new ServerMap(mapsavedir), this),
1065         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1066         m_authmanager(mapsavedir+"/auth.txt"),
1067         m_thread(this),
1068         m_emergethread(this),
1069         m_time_counter(0),
1070         m_time_of_day_send_timer(0),
1071         m_uptime(0),
1072         m_mapsavedir(mapsavedir),
1073         m_configpath(configpath),
1074         m_shutdown_requested(false),
1075         m_ignore_map_edit_events(false),
1076         m_ignore_map_edit_events_peer_id(0)
1077 {
1078         m_liquid_transform_timer = 0.0;
1079         m_print_info_timer = 0.0;
1080         m_objectdata_timer = 0.0;
1081         m_emergethread_trigger_timer = 0.0;
1082         m_savemap_timer = 0.0;
1083         
1084         m_env_mutex.Init();
1085         m_con_mutex.Init();
1086         m_step_dtime_mutex.Init();
1087         m_step_dtime = 0.0;
1088         
1089         // Register us to receive map edit events
1090         m_env.getMap().addEventReceiver(this);
1091
1092         // If file exists, load environment metadata
1093         if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1094         {
1095                 dstream<<"Server: Loading environment metadata"<<std::endl;
1096                 m_env.loadMeta(m_mapsavedir);
1097         }
1098
1099         // Load players
1100         dstream<<"Server: Loading players"<<std::endl;
1101         m_env.deSerializePlayers(m_mapsavedir);
1102 }
1103
1104 Server::~Server()
1105 {
1106         dstream<<"Server::~Server()"<<std::endl;
1107
1108         /*
1109                 Send shutdown message
1110         */
1111         {
1112                 JMutexAutoLock conlock(m_con_mutex);
1113                 
1114                 std::wstring line = L"*** Server shutting down";
1115
1116                 /*
1117                         Send the message to clients
1118                 */
1119                 for(core::map<u16, RemoteClient*>::Iterator
1120                         i = m_clients.getIterator();
1121                         i.atEnd() == false; i++)
1122                 {
1123                         // Get client and check that it is valid
1124                         RemoteClient *client = i.getNode()->getValue();
1125                         assert(client->peer_id == i.getNode()->getKey());
1126                         if(client->serialization_version == SER_FMT_VER_INVALID)
1127                                 continue;
1128
1129                         try{
1130                                 SendChatMessage(client->peer_id, line);
1131                         }
1132                         catch(con::PeerNotFoundException &e)
1133                         {}
1134                 }
1135         }
1136
1137         /*
1138                 Save players
1139         */
1140         dstream<<"Server: Saving players"<<std::endl;
1141         m_env.serializePlayers(m_mapsavedir);
1142
1143         /*
1144                 Save environment metadata
1145         */
1146         dstream<<"Server: Saving environment metadata"<<std::endl;
1147         m_env.saveMeta(m_mapsavedir);
1148         
1149         /*
1150                 Stop threads
1151         */
1152         stop();
1153         
1154         /*
1155                 Delete clients
1156         */
1157         {
1158                 JMutexAutoLock clientslock(m_con_mutex);
1159
1160                 for(core::map<u16, RemoteClient*>::Iterator
1161                         i = m_clients.getIterator();
1162                         i.atEnd() == false; i++)
1163                 {
1164                         /*// Delete player
1165                         // NOTE: These are removed by env destructor
1166                         {
1167                                 u16 peer_id = i.getNode()->getKey();
1168                                 JMutexAutoLock envlock(m_env_mutex);
1169                                 m_env.removePlayer(peer_id);
1170                         }*/
1171                         
1172                         // Delete client
1173                         delete i.getNode()->getValue();
1174                 }
1175         }
1176 }
1177
1178 void Server::start(unsigned short port)
1179 {
1180         DSTACK(__FUNCTION_NAME);
1181         // Stop thread if already running
1182         m_thread.stop();
1183         
1184         // Initialize connection
1185         m_con.setTimeoutMs(30);
1186         m_con.Serve(port);
1187
1188         // Start thread
1189         m_thread.setRun(true);
1190         m_thread.Start();
1191         
1192         dout_server<<"Server: Started on port "<<port<<std::endl;
1193 }
1194
1195 void Server::stop()
1196 {
1197         DSTACK(__FUNCTION_NAME);
1198
1199         // Stop threads (set run=false first so both start stopping)
1200         m_thread.setRun(false);
1201         m_emergethread.setRun(false);
1202         m_thread.stop();
1203         m_emergethread.stop();
1204         
1205         dout_server<<"Server: Threads stopped"<<std::endl;
1206 }
1207
1208 void Server::step(float dtime)
1209 {
1210         DSTACK(__FUNCTION_NAME);
1211         // Limit a bit
1212         if(dtime > 2.0)
1213                 dtime = 2.0;
1214         {
1215                 JMutexAutoLock lock(m_step_dtime_mutex);
1216                 m_step_dtime += dtime;
1217         }
1218 }
1219
1220 void Server::AsyncRunStep()
1221 {
1222         DSTACK(__FUNCTION_NAME);
1223         
1224         float dtime;
1225         {
1226                 JMutexAutoLock lock1(m_step_dtime_mutex);
1227                 dtime = m_step_dtime;
1228         }
1229         
1230         {
1231                 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1232                                 "blocks to clients");
1233                 // Send blocks to clients
1234                 SendBlocks(dtime);
1235         }
1236         
1237         if(dtime < 0.001)
1238                 return;
1239
1240         //dstream<<"Server steps "<<dtime<<std::endl;
1241         //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1242         
1243         {
1244                 JMutexAutoLock lock1(m_step_dtime_mutex);
1245                 m_step_dtime -= dtime;
1246         }
1247
1248         /*
1249                 Update uptime
1250         */
1251         {
1252                 m_uptime.set(m_uptime.get() + dtime);
1253         }
1254         
1255         /*
1256                 Update m_time_of_day and overall game time
1257         */
1258         {
1259                 JMutexAutoLock envlock(m_env_mutex);
1260
1261                 m_time_counter += dtime;
1262                 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1263                 u32 units = (u32)(m_time_counter*speed);
1264                 m_time_counter -= (f32)units / speed;
1265                 
1266                 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1267                 
1268                 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1269
1270                 /*
1271                         Send to clients at constant intervals
1272                 */
1273
1274                 m_time_of_day_send_timer -= dtime;
1275                 if(m_time_of_day_send_timer < 0.0)
1276                 {
1277                         m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1278
1279                         //JMutexAutoLock envlock(m_env_mutex);
1280                         JMutexAutoLock conlock(m_con_mutex);
1281
1282                         for(core::map<u16, RemoteClient*>::Iterator
1283                                 i = m_clients.getIterator();
1284                                 i.atEnd() == false; i++)
1285                         {
1286                                 RemoteClient *client = i.getNode()->getValue();
1287                                 //Player *player = m_env.getPlayer(client->peer_id);
1288                                 
1289                                 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1290                                                 m_env.getTimeOfDay());
1291                                 // Send as reliable
1292                                 m_con.Send(client->peer_id, 0, data, true);
1293                         }
1294                 }
1295         }
1296
1297         {
1298                 // Process connection's timeouts
1299                 JMutexAutoLock lock2(m_con_mutex);
1300                 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1301                 m_con.RunTimeouts(dtime);
1302         }
1303         
1304         {
1305                 // This has to be called so that the client list gets synced
1306                 // with the peer list of the connection
1307                 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1308                 handlePeerChanges();
1309         }
1310
1311         {
1312                 JMutexAutoLock lock(m_env_mutex);
1313                 // Step environment
1314                 ScopeProfiler sp(&g_profiler, "Server: environment step");
1315                 m_env.step(dtime);
1316         }
1317                 
1318         const float map_timer_and_unload_dtime = 5.15;
1319         if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1320         {
1321                 JMutexAutoLock lock(m_env_mutex);
1322                 // Run Map's timers and unload unused data
1323                 ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
1324                 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1325                                 g_settings.getFloat("server_unload_unused_data_timeout"));
1326         }
1327         
1328         /*
1329                 Do background stuff
1330         */
1331         
1332         /*
1333                 Transform liquids
1334         */
1335         m_liquid_transform_timer += dtime;
1336         if(m_liquid_transform_timer >= 1.00)
1337         {
1338                 m_liquid_transform_timer -= 1.00;
1339                 
1340                 JMutexAutoLock lock(m_env_mutex);
1341
1342                 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1343
1344                 core::map<v3s16, MapBlock*> modified_blocks;
1345                 m_env.getMap().transformLiquids(modified_blocks);
1346 #if 0           
1347                 /*
1348                         Update lighting
1349                 */
1350                 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1351                 ServerMap &map = ((ServerMap&)m_env.getMap());
1352                 map.updateLighting(modified_blocks, lighting_modified_blocks);
1353                 
1354                 // Add blocks modified by lighting to modified_blocks
1355                 for(core::map<v3s16, MapBlock*>::Iterator
1356                                 i = lighting_modified_blocks.getIterator();
1357                                 i.atEnd() == false; i++)
1358                 {
1359                         MapBlock *block = i.getNode()->getValue();
1360                         modified_blocks.insert(block->getPos(), block);
1361                 }
1362 #endif
1363                 /*
1364                         Set the modified blocks unsent for all the clients
1365                 */
1366                 
1367                 JMutexAutoLock lock2(m_con_mutex);
1368
1369                 for(core::map<u16, RemoteClient*>::Iterator
1370                                 i = m_clients.getIterator();
1371                                 i.atEnd() == false; i++)
1372                 {
1373                         RemoteClient *client = i.getNode()->getValue();
1374                         
1375                         if(modified_blocks.size() > 0)
1376                         {
1377                                 // Remove block from sent history
1378                                 client->SetBlocksNotSent(modified_blocks);
1379                         }
1380                 }
1381         }
1382
1383         // Periodically print some info
1384         {
1385                 float &counter = m_print_info_timer;
1386                 counter += dtime;
1387                 if(counter >= 30.0)
1388                 {
1389                         counter = 0.0;
1390
1391                         JMutexAutoLock lock2(m_con_mutex);
1392
1393                         for(core::map<u16, RemoteClient*>::Iterator
1394                                 i = m_clients.getIterator();
1395                                 i.atEnd() == false; i++)
1396                         {
1397                                 //u16 peer_id = i.getNode()->getKey();
1398                                 RemoteClient *client = i.getNode()->getValue();
1399                                 Player *player = m_env.getPlayer(client->peer_id);
1400                                 if(player==NULL)
1401                                         continue;
1402                                 std::cout<<player->getName()<<"\t";
1403                                 client->PrintInfo(std::cout);
1404                         }
1405                 }
1406         }
1407
1408         //if(g_settings.getBool("enable_experimental"))
1409         {
1410
1411         /*
1412                 Check added and deleted active objects
1413         */
1414         {
1415                 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1416                 JMutexAutoLock envlock(m_env_mutex);
1417                 JMutexAutoLock conlock(m_con_mutex);
1418
1419                 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1420
1421                 // Radius inside which objects are active
1422                 s16 radius = 32;
1423
1424                 for(core::map<u16, RemoteClient*>::Iterator
1425                         i = m_clients.getIterator();
1426                         i.atEnd() == false; i++)
1427                 {
1428                         RemoteClient *client = i.getNode()->getValue();
1429                         Player *player = m_env.getPlayer(client->peer_id);
1430                         if(player==NULL)
1431                         {
1432                                 // This can happen if the client timeouts somehow
1433                                 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1434                                                 <<client->peer_id
1435                                                 <<" has no associated player"<<std::endl;*/
1436                                 continue;
1437                         }
1438                         v3s16 pos = floatToInt(player->getPosition(), BS);
1439
1440                         core::map<u16, bool> removed_objects;
1441                         core::map<u16, bool> added_objects;
1442                         m_env.getRemovedActiveObjects(pos, radius,
1443                                         client->m_known_objects, removed_objects);
1444                         m_env.getAddedActiveObjects(pos, radius,
1445                                         client->m_known_objects, added_objects);
1446                         
1447                         // Ignore if nothing happened
1448                         if(removed_objects.size() == 0 && added_objects.size() == 0)
1449                         {
1450                                 //dstream<<"INFO: active objects: none changed"<<std::endl;
1451                                 continue;
1452                         }
1453                         
1454                         std::string data_buffer;
1455
1456                         char buf[4];
1457                         
1458                         // Handle removed objects
1459                         writeU16((u8*)buf, removed_objects.size());
1460                         data_buffer.append(buf, 2);
1461                         for(core::map<u16, bool>::Iterator
1462                                         i = removed_objects.getIterator();
1463                                         i.atEnd()==false; i++)
1464                         {
1465                                 // Get object
1466                                 u16 id = i.getNode()->getKey();
1467                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1468
1469                                 // Add to data buffer for sending
1470                                 writeU16((u8*)buf, i.getNode()->getKey());
1471                                 data_buffer.append(buf, 2);
1472                                 
1473                                 // Remove from known objects
1474                                 client->m_known_objects.remove(i.getNode()->getKey());
1475
1476                                 if(obj && obj->m_known_by_count > 0)
1477                                         obj->m_known_by_count--;
1478                         }
1479
1480                         // Handle added objects
1481                         writeU16((u8*)buf, added_objects.size());
1482                         data_buffer.append(buf, 2);
1483                         for(core::map<u16, bool>::Iterator
1484                                         i = added_objects.getIterator();
1485                                         i.atEnd()==false; i++)
1486                         {
1487                                 // Get object
1488                                 u16 id = i.getNode()->getKey();
1489                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1490                                 
1491                                 // Get object type
1492                                 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1493                                 if(obj == NULL)
1494                                         dstream<<"WARNING: "<<__FUNCTION_NAME
1495                                                         <<": NULL object"<<std::endl;
1496                                 else
1497                                         type = obj->getType();
1498
1499                                 // Add to data buffer for sending
1500                                 writeU16((u8*)buf, id);
1501                                 data_buffer.append(buf, 2);
1502                                 writeU8((u8*)buf, type);
1503                                 data_buffer.append(buf, 1);
1504                                 
1505                                 if(obj)
1506                                         data_buffer.append(serializeLongString(
1507                                                         obj->getClientInitializationData()));
1508                                 else
1509                                         data_buffer.append(serializeLongString(""));
1510
1511                                 // Add to known objects
1512                                 client->m_known_objects.insert(i.getNode()->getKey(), false);
1513
1514                                 if(obj)
1515                                         obj->m_known_by_count++;
1516                         }
1517
1518                         // Send packet
1519                         SharedBuffer<u8> reply(2 + data_buffer.size());
1520                         writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1521                         memcpy((char*)&reply[2], data_buffer.c_str(),
1522                                         data_buffer.size());
1523                         // Send as reliable
1524                         m_con.Send(client->peer_id, 0, reply, true);
1525
1526                         dstream<<"INFO: Server: Sent object remove/add: "
1527                                         <<removed_objects.size()<<" removed, "
1528                                         <<added_objects.size()<<" added, "
1529                                         <<"packet size is "<<reply.getSize()<<std::endl;
1530                 }
1531
1532 #if 0
1533                 /*
1534                         Collect a list of all the objects known by the clients
1535                         and report it back to the environment.
1536                 */
1537
1538                 core::map<u16, bool> all_known_objects;
1539
1540                 for(core::map<u16, RemoteClient*>::Iterator
1541                         i = m_clients.getIterator();
1542                         i.atEnd() == false; i++)
1543                 {
1544                         RemoteClient *client = i.getNode()->getValue();
1545                         // Go through all known objects of client
1546                         for(core::map<u16, bool>::Iterator
1547                                         i = client->m_known_objects.getIterator();
1548                                         i.atEnd()==false; i++)
1549                         {
1550                                 u16 id = i.getNode()->getKey();
1551                                 all_known_objects[id] = true;
1552                         }
1553                 }
1554                 
1555                 m_env.setKnownActiveObjects(whatever);
1556 #endif
1557
1558         }
1559
1560         /*
1561                 Send object messages
1562         */
1563         {
1564                 JMutexAutoLock envlock(m_env_mutex);
1565                 JMutexAutoLock conlock(m_con_mutex);
1566
1567                 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1568
1569                 // Key = object id
1570                 // Value = data sent by object
1571                 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1572
1573                 // Get active object messages from environment
1574                 for(;;)
1575                 {
1576                         ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1577                         if(aom.id == 0)
1578                                 break;
1579                         
1580                         core::list<ActiveObjectMessage>* message_list = NULL;
1581                         core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1582                         n = buffered_messages.find(aom.id);
1583                         if(n == NULL)
1584                         {
1585                                 message_list = new core::list<ActiveObjectMessage>;
1586                                 buffered_messages.insert(aom.id, message_list);
1587                         }
1588                         else
1589                         {
1590                                 message_list = n->getValue();
1591                         }
1592                         message_list->push_back(aom);
1593                 }
1594                 
1595                 // Route data to every client
1596                 for(core::map<u16, RemoteClient*>::Iterator
1597                         i = m_clients.getIterator();
1598                         i.atEnd()==false; i++)
1599                 {
1600                         RemoteClient *client = i.getNode()->getValue();
1601                         std::string reliable_data;
1602                         std::string unreliable_data;
1603                         // Go through all objects in message buffer
1604                         for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1605                                         j = buffered_messages.getIterator();
1606                                         j.atEnd()==false; j++)
1607                         {
1608                                 // If object is not known by client, skip it
1609                                 u16 id = j.getNode()->getKey();
1610                                 if(client->m_known_objects.find(id) == NULL)
1611                                         continue;
1612                                 // Get message list of object
1613                                 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1614                                 // Go through every message
1615                                 for(core::list<ActiveObjectMessage>::Iterator
1616                                                 k = list->begin(); k != list->end(); k++)
1617                                 {
1618                                         // Compose the full new data with header
1619                                         ActiveObjectMessage aom = *k;
1620                                         std::string new_data;
1621                                         // Add object id
1622                                         char buf[2];
1623                                         writeU16((u8*)&buf[0], aom.id);
1624                                         new_data.append(buf, 2);
1625                                         // Add data
1626                                         new_data += serializeString(aom.datastring);
1627                                         // Add data to buffer
1628                                         if(aom.reliable)
1629                                                 reliable_data += new_data;
1630                                         else
1631                                                 unreliable_data += new_data;
1632                                 }
1633                         }
1634                         /*
1635                                 reliable_data and unreliable_data are now ready.
1636                                 Send them.
1637                         */
1638                         if(reliable_data.size() > 0)
1639                         {
1640                                 SharedBuffer<u8> reply(2 + reliable_data.size());
1641                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1642                                 memcpy((char*)&reply[2], reliable_data.c_str(),
1643                                                 reliable_data.size());
1644                                 // Send as reliable
1645                                 m_con.Send(client->peer_id, 0, reply, true);
1646                         }
1647                         if(unreliable_data.size() > 0)
1648                         {
1649                                 SharedBuffer<u8> reply(2 + unreliable_data.size());
1650                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1651                                 memcpy((char*)&reply[2], unreliable_data.c_str(),
1652                                                 unreliable_data.size());
1653                                 // Send as unreliable
1654                                 m_con.Send(client->peer_id, 0, reply, false);
1655                         }
1656
1657                         /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1658                         {
1659                                 dstream<<"INFO: Server: Size of object message data: "
1660                                                 <<"reliable: "<<reliable_data.size()
1661                                                 <<", unreliable: "<<unreliable_data.size()
1662                                                 <<std::endl;
1663                         }*/
1664                 }
1665
1666                 // Clear buffered_messages
1667                 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1668                                 i = buffered_messages.getIterator();
1669                                 i.atEnd()==false; i++)
1670                 {
1671                         delete i.getNode()->getValue();
1672                 }
1673         }
1674
1675         } // enable_experimental
1676
1677         /*
1678                 Send queued-for-sending map edit events.
1679         */
1680         {
1681                 // Don't send too many at a time
1682                 //u32 count = 0;
1683
1684                 // Single change sending is disabled if queue size is not small
1685                 bool disable_single_change_sending = false;
1686                 if(m_unsent_map_edit_queue.size() >= 4)
1687                         disable_single_change_sending = true;
1688
1689                 bool got_any_events = false;
1690
1691                 // We'll log the amount of each
1692                 Profiler prof;
1693
1694                 while(m_unsent_map_edit_queue.size() != 0)
1695                 {
1696                         got_any_events = true;
1697
1698                         MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1699                         
1700                         // Players far away from the change are stored here.
1701                         // Instead of sending the changes, MapBlocks are set not sent
1702                         // for them.
1703                         core::list<u16> far_players;
1704
1705                         if(event->type == MEET_ADDNODE)
1706                         {
1707                                 //dstream<<"Server: MEET_ADDNODE"<<std::endl;
1708                                 prof.add("MEET_ADDNODE", 1);
1709                                 if(disable_single_change_sending)
1710                                         sendAddNode(event->p, event->n, event->already_known_by_peer,
1711                                                         &far_players, 5);
1712                                 else
1713                                         sendAddNode(event->p, event->n, event->already_known_by_peer,
1714                                                         &far_players, 30);
1715                         }
1716                         else if(event->type == MEET_REMOVENODE)
1717                         {
1718                                 //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1719                                 prof.add("MEET_REMOVENODE", 1);
1720                                 if(disable_single_change_sending)
1721                                         sendRemoveNode(event->p, event->already_known_by_peer,
1722                                                         &far_players, 5);
1723                                 else
1724                                         sendRemoveNode(event->p, event->already_known_by_peer,
1725                                                         &far_players, 30);
1726                         }
1727                         else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1728                         {
1729                                 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1730                                 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1731                                 setBlockNotSent(event->p);
1732                         }
1733                         else if(event->type == MEET_OTHER)
1734                         {
1735                                 prof.add("MEET_OTHER", 1);
1736                                 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1737                                                 <<std::endl;
1738                         }
1739                         else
1740                         {
1741                                 prof.add("unknown", 1);
1742                                 dstream<<"WARNING: Server: Unknown MapEditEvent "
1743                                                 <<((u32)event->type)<<std::endl;
1744                         }
1745                         
1746                         /*
1747                                 Set blocks not sent to far players
1748                         */
1749                         if(far_players.size() > 0)
1750                         {
1751                                 // Convert list format to that wanted by SetBlocksNotSent
1752                                 core::map<v3s16, MapBlock*> modified_blocks2;
1753                                 for(core::map<v3s16, bool>::Iterator
1754                                                 i = event->modified_blocks.getIterator();
1755                                                 i.atEnd()==false; i++)
1756                                 {
1757                                         v3s16 p = i.getNode()->getKey();
1758                                         modified_blocks2.insert(p,
1759                                                         m_env.getMap().getBlockNoCreateNoEx(p));
1760                                 }
1761                                 // Set blocks not sent
1762                                 for(core::list<u16>::Iterator
1763                                                 i = far_players.begin();
1764                                                 i != far_players.end(); i++)
1765                                 {
1766                                         u16 peer_id = *i;
1767                                         RemoteClient *client = getClient(peer_id);
1768                                         if(client==NULL)
1769                                                 continue;
1770                                         client->SetBlocksNotSent(modified_blocks2);
1771                                 }
1772                         }
1773
1774                         delete event;
1775
1776                         /*// Don't send too many at a time
1777                         count++;
1778                         if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1779                                 break;*/
1780                 }
1781
1782                 if(got_any_events)
1783                 {
1784                         dstream<<"Server: MapEditEvents:"<<std::endl;
1785                         prof.print(dstream);
1786                 }
1787                 
1788         }
1789
1790         /*
1791                 Send object positions
1792                 TODO: Get rid of MapBlockObjects
1793         */
1794         {
1795                 float &counter = m_objectdata_timer;
1796                 counter += dtime;
1797                 if(counter >= g_settings.getFloat("objectdata_interval"))
1798                 {
1799                         JMutexAutoLock lock1(m_env_mutex);
1800                         JMutexAutoLock lock2(m_con_mutex);
1801
1802                         ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1803
1804                         SendObjectData(counter);
1805
1806                         counter = 0.0;
1807                 }
1808         }
1809         
1810         /*
1811                 Trigger emergethread (it somehow gets to a non-triggered but
1812                 bysy state sometimes)
1813         */
1814         {
1815                 float &counter = m_emergethread_trigger_timer;
1816                 counter += dtime;
1817                 if(counter >= 2.0)
1818                 {
1819                         counter = 0.0;
1820                         
1821                         m_emergethread.trigger();
1822                 }
1823         }
1824
1825         // Save map, players and auth stuff
1826         {
1827                 float &counter = m_savemap_timer;
1828                 counter += dtime;
1829                 if(counter >= g_settings.getFloat("server_map_save_interval"))
1830                 {
1831                         counter = 0.0;
1832
1833                         ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1834
1835                         // Auth stuff
1836                         if(m_authmanager.isModified())
1837                                 m_authmanager.save();
1838                         
1839                         // Map
1840                         JMutexAutoLock lock(m_env_mutex);
1841
1842                         /*// Unload unused data (delete from memory)
1843                         m_env.getMap().unloadUnusedData(
1844                                         g_settings.getFloat("server_unload_unused_sectors_timeout"));
1845                                         */
1846                         /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1847                                         g_settings.getFloat("server_unload_unused_sectors_timeout"));
1848                                         */
1849
1850                         // Save only changed parts
1851                         m_env.getMap().save(true);
1852
1853                         /*if(deleted_count > 0)
1854                         {
1855                                 dout_server<<"Server: Unloaded "<<deleted_count
1856                                                 <<" blocks from memory"<<std::endl;
1857                         }*/
1858
1859                         // Save players
1860                         m_env.serializePlayers(m_mapsavedir);
1861                         
1862                         // Save environment metadata
1863                         m_env.saveMeta(m_mapsavedir);
1864                 }
1865         }
1866 }
1867
1868 void Server::Receive()
1869 {
1870         DSTACK(__FUNCTION_NAME);
1871         u32 data_maxsize = 10000;
1872         Buffer<u8> data(data_maxsize);
1873         u16 peer_id;
1874         u32 datasize;
1875         try{
1876                 {
1877                         JMutexAutoLock conlock(m_con_mutex);
1878                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1879                 }
1880
1881                 // This has to be called so that the client list gets synced
1882                 // with the peer list of the connection
1883                 handlePeerChanges();
1884
1885                 ProcessData(*data, datasize, peer_id);
1886         }
1887         catch(con::InvalidIncomingDataException &e)
1888         {
1889                 derr_server<<"Server::Receive(): "
1890                                 "InvalidIncomingDataException: what()="
1891                                 <<e.what()<<std::endl;
1892         }
1893         catch(con::PeerNotFoundException &e)
1894         {
1895                 //NOTE: This is not needed anymore
1896                 
1897                 // The peer has been disconnected.
1898                 // Find the associated player and remove it.
1899
1900                 /*JMutexAutoLock envlock(m_env_mutex);
1901
1902                 dout_server<<"ServerThread: peer_id="<<peer_id
1903                                 <<" has apparently closed connection. "
1904                                 <<"Removing player."<<std::endl;
1905
1906                 m_env.removePlayer(peer_id);*/
1907         }
1908 }
1909
1910 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1911 {
1912         DSTACK(__FUNCTION_NAME);
1913         // Environment is locked first.
1914         JMutexAutoLock envlock(m_env_mutex);
1915         JMutexAutoLock conlock(m_con_mutex);
1916         
1917         con::Peer *peer;
1918         try{
1919                 peer = m_con.GetPeer(peer_id);
1920         }
1921         catch(con::PeerNotFoundException &e)
1922         {
1923                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1924                                 <<peer_id<<" not found"<<std::endl;
1925                 return;
1926         }
1927         
1928         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1929
1930         try
1931         {
1932
1933         if(datasize < 2)
1934                 return;
1935
1936         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1937         
1938         if(command == TOSERVER_INIT)
1939         {
1940                 // [0] u16 TOSERVER_INIT
1941                 // [2] u8 SER_FMT_VER_HIGHEST
1942                 // [3] u8[20] player_name
1943                 // [23] u8[28] password <--- can be sent without this, from old versions
1944
1945                 if(datasize < 2+1+PLAYERNAME_SIZE)
1946                         return;
1947
1948                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1949                                 <<peer->id<<std::endl;
1950
1951                 // First byte after command is maximum supported
1952                 // serialization version
1953                 u8 client_max = data[2];
1954                 u8 our_max = SER_FMT_VER_HIGHEST;
1955                 // Use the highest version supported by both
1956                 u8 deployed = core::min_(client_max, our_max);
1957                 // If it's lower than the lowest supported, give up.
1958                 if(deployed < SER_FMT_VER_LOWEST)
1959                         deployed = SER_FMT_VER_INVALID;
1960
1961                 //peer->serialization_version = deployed;
1962                 getClient(peer->id)->pending_serialization_version = deployed;
1963
1964                 if(deployed == SER_FMT_VER_INVALID)
1965                 {
1966                         derr_server<<DTIME<<"Server: Cannot negotiate "
1967                                         "serialization version with peer "
1968                                         <<peer_id<<std::endl;
1969                         SendAccessDenied(m_con, peer_id,
1970                                         L"Your client is too old (map format)");
1971                         return;
1972                 }
1973                 
1974                 /*
1975                         Read and check network protocol version
1976                 */
1977
1978                 u16 net_proto_version = 0;
1979                 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1980                 {
1981                         net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1982                 }
1983
1984                 getClient(peer->id)->net_proto_version = net_proto_version;
1985
1986                 if(net_proto_version == 0)
1987                 {
1988                         SendAccessDenied(m_con, peer_id,
1989                                         L"Your client is too old. Please upgrade.");
1990                         return;
1991                 }
1992
1993                 /*
1994                         Set up player
1995                 */
1996                 
1997                 // Get player name
1998                 char playername[PLAYERNAME_SIZE];
1999                 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2000                 {
2001                         playername[i] = data[3+i];
2002                 }
2003                 playername[PLAYERNAME_SIZE-1] = 0;
2004                 
2005                 if(playername[0]=='\0')
2006                 {
2007                         derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
2008                         SendAccessDenied(m_con, peer_id,
2009                                         L"Empty name");
2010                         return;
2011                 }
2012
2013                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2014                 {
2015                         derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
2016                         SendAccessDenied(m_con, peer_id,
2017                                         L"Name contains unallowed characters");
2018                         return;
2019                 }
2020
2021                 // Get password
2022                 char password[PASSWORD_SIZE];
2023                 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2024                 {
2025                         // old version - assume blank password
2026                         password[0] = 0;
2027                 }
2028                 else
2029                 {
2030                                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2031                                 {
2032                                         password[i] = data[23+i];
2033                                 }
2034                                 password[PASSWORD_SIZE-1] = 0;
2035                 }
2036                 
2037                 std::string checkpwd;
2038                 if(m_authmanager.exists(playername))
2039                 {
2040                         checkpwd = m_authmanager.getPassword(playername);
2041                 }
2042                 else
2043                 {
2044                         checkpwd = g_settings.get("default_password");
2045                 }
2046                 
2047                 /*dstream<<"Server: Client gave password '"<<password
2048                                 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2049                 
2050                 if(password != checkpwd && m_authmanager.exists(playername))
2051                 {
2052                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
2053                                         <<": supplied invalid password for "
2054                                         <<playername<<std::endl;
2055                         SendAccessDenied(m_con, peer_id, L"Invalid password");
2056                         return;
2057                 }
2058                 
2059                 // Add player to auth manager
2060                 if(m_authmanager.exists(playername) == false)
2061                 {
2062                         derr_server<<DTIME<<"Server: adding player "<<playername
2063                                         <<" to auth manager"<<std::endl;
2064                         m_authmanager.add(playername);
2065                         m_authmanager.setPassword(playername, checkpwd);
2066                         m_authmanager.setPrivs(playername,
2067                                         stringToPrivs(g_settings.get("default_privs")));
2068                         m_authmanager.save();
2069                 }
2070
2071                 // Get player
2072                 Player *player = emergePlayer(playername, password, peer_id);
2073
2074
2075                 /*{
2076                         // DEBUG: Test serialization
2077                         std::ostringstream test_os;
2078                         player->serialize(test_os);
2079                         dstream<<"Player serialization test: \""<<test_os.str()
2080                                         <<"\""<<std::endl;
2081                         std::istringstream test_is(test_os.str());
2082                         player->deSerialize(test_is);
2083                 }*/
2084
2085                 // If failed, cancel
2086                 if(player == NULL)
2087                 {
2088                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
2089                                         <<": failed to emerge player"<<std::endl;
2090                         return;
2091                 }
2092
2093                 /*
2094                 // If a client is already connected to the player, cancel
2095                 if(player->peer_id != 0)
2096                 {
2097                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
2098                                         <<" tried to connect to "
2099                                         "an already connected player (peer_id="
2100                                         <<player->peer_id<<")"<<std::endl;
2101                         return;
2102                 }
2103                 // Set client of player
2104                 player->peer_id = peer_id;
2105                 */
2106
2107                 // Check if player doesn't exist
2108                 if(player == NULL)
2109                         throw con::InvalidIncomingDataException
2110                                 ("Server::ProcessData(): INIT: Player doesn't exist");
2111
2112                 /*// update name if it was supplied
2113                 if(datasize >= 20+3)
2114                 {
2115                         data[20+3-1] = 0;
2116                         player->updateName((const char*)&data[3]);
2117                 }*/
2118                 
2119                 /*
2120                         Answer with a TOCLIENT_INIT
2121                 */
2122                 {
2123                         SharedBuffer<u8> reply(2+1+6+8);
2124                         writeU16(&reply[0], TOCLIENT_INIT);
2125                         writeU8(&reply[2], deployed);
2126                         writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2127                         writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2128                         
2129                         // Send as reliable
2130                         m_con.Send(peer_id, 0, reply, true);
2131                 }
2132
2133                 /*
2134                         Send complete position information
2135                 */
2136                 SendMovePlayer(player);
2137
2138                 return;
2139         }
2140
2141         if(command == TOSERVER_INIT2)
2142         {
2143                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2144                                 <<peer->id<<std::endl;
2145
2146
2147                 getClient(peer->id)->serialization_version
2148                                 = getClient(peer->id)->pending_serialization_version;
2149
2150                 /*
2151                         Send some initialization data
2152                 */
2153                 
2154                 // Send player info to all players
2155                 SendPlayerInfos();
2156
2157                 // Send inventory to player
2158                 UpdateCrafting(peer->id);
2159                 SendInventory(peer->id);
2160
2161                 // Send HP
2162                 {
2163                         Player *player = m_env.getPlayer(peer_id);
2164                         SendPlayerHP(player);
2165                 }
2166                 
2167                 // Send time of day
2168                 {
2169                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2170                                         m_env.getTimeOfDay());
2171                         m_con.Send(peer->id, 0, data, true);
2172                 }
2173                 
2174                 // Send information about server to player in chat
2175                 SendChatMessage(peer_id, getStatusString());
2176                 
2177                 // Send information about joining in chat
2178                 {
2179                         std::wstring name = L"unknown";
2180                         Player *player = m_env.getPlayer(peer_id);
2181                         if(player != NULL)
2182                                 name = narrow_to_wide(player->getName());
2183                         
2184                         std::wstring message;
2185                         message += L"*** ";
2186                         message += name;
2187                         message += L" joined game";
2188                         BroadcastChatMessage(message);
2189                 }
2190                 
2191                 // Warnings about protocol version can be issued here
2192                 /*if(getClient(peer->id)->net_proto_version == 0)
2193                 {
2194                         SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2195                 }*/
2196
2197                 return;
2198         }
2199
2200         if(peer_ser_ver == SER_FMT_VER_INVALID)
2201         {
2202                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2203                                 " serialization format invalid or not initialized."
2204                                 " Skipping incoming command="<<command<<std::endl;
2205                 return;
2206         }
2207         
2208         Player *player = m_env.getPlayer(peer_id);
2209
2210         if(player == NULL){
2211                 derr_server<<"Server::ProcessData(): Cancelling: "
2212                                 "No player for peer_id="<<peer_id
2213                                 <<std::endl;
2214                 return;
2215         }
2216         if(command == TOSERVER_PLAYERPOS)
2217         {
2218                 if(datasize < 2+12+12+4+4)
2219                         return;
2220         
2221                 u32 start = 0;
2222                 v3s32 ps = readV3S32(&data[start+2]);
2223                 v3s32 ss = readV3S32(&data[start+2+12]);
2224                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2225                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2226                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2227                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2228                 pitch = wrapDegrees(pitch);
2229                 yaw = wrapDegrees(yaw);
2230                 player->setPosition(position);
2231                 player->setSpeed(speed);
2232                 player->setPitch(pitch);
2233                 player->setYaw(yaw);
2234                 
2235                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2236                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2237                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2238         }
2239         else if(command == TOSERVER_GOTBLOCKS)
2240         {
2241                 if(datasize < 2+1)
2242                         return;
2243                 
2244                 /*
2245                         [0] u16 command
2246                         [2] u8 count
2247                         [3] v3s16 pos_0
2248                         [3+6] v3s16 pos_1
2249                         ...
2250                 */
2251
2252                 u16 count = data[2];
2253                 for(u16 i=0; i<count; i++)
2254                 {
2255                         if((s16)datasize < 2+1+(i+1)*6)
2256                                 throw con::InvalidIncomingDataException
2257                                         ("GOTBLOCKS length is too short");
2258                         v3s16 p = readV3S16(&data[2+1+i*6]);
2259                         /*dstream<<"Server: GOTBLOCKS ("
2260                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2261                         RemoteClient *client = getClient(peer_id);
2262                         client->GotBlock(p);
2263                 }
2264         }
2265         else if(command == TOSERVER_DELETEDBLOCKS)
2266         {
2267                 if(datasize < 2+1)
2268                         return;
2269                 
2270                 /*
2271                         [0] u16 command
2272                         [2] u8 count
2273                         [3] v3s16 pos_0
2274                         [3+6] v3s16 pos_1
2275                         ...
2276                 */
2277
2278                 u16 count = data[2];
2279                 for(u16 i=0; i<count; i++)
2280                 {
2281                         if((s16)datasize < 2+1+(i+1)*6)
2282                                 throw con::InvalidIncomingDataException
2283                                         ("DELETEDBLOCKS length is too short");
2284                         v3s16 p = readV3S16(&data[2+1+i*6]);
2285                         /*dstream<<"Server: DELETEDBLOCKS ("
2286                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2287                         RemoteClient *client = getClient(peer_id);
2288                         client->SetBlockNotSent(p);
2289                 }
2290         }
2291         else if(command == TOSERVER_CLICK_OBJECT)
2292         {
2293                 if(datasize < 13)
2294                         return;
2295
2296                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2297                         return;
2298
2299                 /*
2300                         [0] u16 command
2301                         [2] u8 button (0=left, 1=right)
2302                         [3] v3s16 block
2303                         [9] s16 id
2304                         [11] u16 item
2305                 */
2306                 u8 button = readU8(&data[2]);
2307                 v3s16 p;
2308                 p.X = readS16(&data[3]);
2309                 p.Y = readS16(&data[5]);
2310                 p.Z = readS16(&data[7]);
2311                 s16 id = readS16(&data[9]);
2312                 //u16 item_i = readU16(&data[11]);
2313
2314                 MapBlock *block = NULL;
2315                 try
2316                 {
2317                         block = m_env.getMap().getBlockNoCreate(p);
2318                 }
2319                 catch(InvalidPositionException &e)
2320                 {
2321                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2322                         return;
2323                 }
2324
2325                 MapBlockObject *obj = block->getObject(id);
2326
2327                 if(obj == NULL)
2328                 {
2329                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2330                         return;
2331                 }
2332
2333                 //TODO: Check that object is reasonably close
2334                 
2335                 // Left click
2336                 if(button == 0)
2337                 {
2338                         InventoryList *ilist = player->inventory.getList("main");
2339                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2340                         {
2341                         
2342                                 // Skip if inventory has no free space
2343                                 if(ilist->getUsedSlots() == ilist->getSize())
2344                                 {
2345                                         dout_server<<"Player inventory has no free space"<<std::endl;
2346                                         return;
2347                                 }
2348                                 
2349                                 /*
2350                                         Create the inventory item
2351                                 */
2352                                 InventoryItem *item = NULL;
2353                                 // If it is an item-object, take the item from it
2354                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2355                                 {
2356                                         item = ((ItemObject*)obj)->createInventoryItem();
2357                                 }
2358                                 // Else create an item of the object
2359                                 else
2360                                 {
2361                                         item = new MapBlockObjectItem
2362                                                         (obj->getInventoryString());
2363                                 }
2364                                 
2365                                 // Add to inventory and send inventory
2366                                 ilist->addItem(item);
2367                                 UpdateCrafting(player->peer_id);
2368                                 SendInventory(player->peer_id);
2369                         }
2370
2371                         // Remove from block
2372                         block->removeObject(id);
2373                 }
2374         }
2375         else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2376         {
2377                 if(datasize < 7)
2378                         return;
2379
2380                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2381                         return;
2382
2383                 /*
2384                         length: 7
2385                         [0] u16 command
2386                         [2] u8 button (0=left, 1=right)
2387                         [3] u16 id
2388                         [5] u16 item
2389                 */
2390                 u8 button = readU8(&data[2]);
2391                 u16 id = readS16(&data[3]);
2392                 u16 item_i = readU16(&data[11]);
2393         
2394                 ServerActiveObject *obj = m_env.getActiveObject(id);
2395
2396                 if(obj == NULL)
2397                 {
2398                         derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2399                                         <<std::endl;
2400                         return;
2401                 }
2402
2403                 // Skip if object has been removed
2404                 if(obj->m_removed)
2405                         return;
2406                 
2407                 //TODO: Check that object is reasonably close
2408                 
2409                 // Left click, pick object up (usually)
2410                 if(button == 0)
2411                 {
2412                         /*
2413                                 Try creating inventory item
2414                         */
2415                         InventoryItem *item = obj->createPickedUpItem();
2416                         
2417                         if(item)
2418                         {
2419                                 InventoryList *ilist = player->inventory.getList("main");
2420                                 if(ilist != NULL)
2421                                 {
2422                                         if(g_settings.getBool("creative_mode") == false)
2423                                         {
2424                                                 // Skip if inventory has no free space
2425                                                 if(ilist->getUsedSlots() == ilist->getSize())
2426                                                 {
2427                                                         dout_server<<"Player inventory has no free space"<<std::endl;
2428                                                         return;
2429                                                 }
2430
2431                                                 // Add to inventory and send inventory
2432                                                 ilist->addItem(item);
2433                                                 UpdateCrafting(player->peer_id);
2434                                                 SendInventory(player->peer_id);
2435                                         }
2436
2437                                         // Remove object from environment
2438                                         obj->m_removed = true;
2439                                 }
2440                         }
2441                         else
2442                         {
2443                                 /*
2444                                         Item cannot be picked up. Punch it instead.
2445                                 */
2446
2447                                 ToolItem *titem = NULL;
2448                                 std::string toolname = "";
2449
2450                                 InventoryList *mlist = player->inventory.getList("main");
2451                                 if(mlist != NULL)
2452                                 {
2453                                         InventoryItem *item = mlist->getItem(item_i);
2454                                         if(item && (std::string)item->getName() == "ToolItem")
2455                                         {
2456                                                 titem = (ToolItem*)item;
2457                                                 toolname = titem->getToolName();
2458                                         }
2459                                 }
2460
2461                                 v3f playerpos = player->getPosition();
2462                                 v3f objpos = obj->getBasePosition();
2463                                 v3f dir = (objpos - playerpos).normalize();
2464                                 
2465                                 u16 wear = obj->punch(toolname, dir);
2466                                 
2467                                 if(titem)
2468                                 {
2469                                         bool weared_out = titem->addWear(wear);
2470                                         if(weared_out)
2471                                                 mlist->deleteItem(item_i);
2472                                         SendInventory(player->peer_id);
2473                                 }
2474                         }
2475                 }
2476                 // Right click, do something with object
2477                 if(button == 1)
2478                 {
2479                         // Track hp changes super-crappily
2480                         u16 oldhp = player->hp;
2481                         
2482                         // Do stuff
2483                         obj->rightClick(player);
2484                         
2485                         // Send back stuff
2486                         if(player->hp != oldhp)
2487                         {
2488                                 SendPlayerHP(player);
2489                         }
2490                 }
2491         }
2492         else if(command == TOSERVER_GROUND_ACTION)
2493         {
2494                 if(datasize < 17)
2495                         return;
2496                 /*
2497                         length: 17
2498                         [0] u16 command
2499                         [2] u8 action
2500                         [3] v3s16 nodepos_undersurface
2501                         [9] v3s16 nodepos_abovesurface
2502                         [15] u16 item
2503                         actions:
2504                         0: start digging
2505                         1: place block
2506                         2: stop digging (all parameters ignored)
2507                         3: digging completed
2508                 */
2509                 u8 action = readU8(&data[2]);
2510                 v3s16 p_under;
2511                 p_under.X = readS16(&data[3]);
2512                 p_under.Y = readS16(&data[5]);
2513                 p_under.Z = readS16(&data[7]);
2514                 v3s16 p_over;
2515                 p_over.X = readS16(&data[9]);
2516                 p_over.Y = readS16(&data[11]);
2517                 p_over.Z = readS16(&data[13]);
2518                 u16 item_i = readU16(&data[15]);
2519
2520                 //TODO: Check that target is reasonably close
2521                 
2522                 /*
2523                         0: start digging
2524                 */
2525                 if(action == 0)
2526                 {
2527                         /*
2528                                 NOTE: This can be used in the future to check if
2529                                 somebody is cheating, by checking the timing.
2530                         */
2531                 } // action == 0
2532
2533                 /*
2534                         2: stop digging
2535                 */
2536                 else if(action == 2)
2537                 {
2538 #if 0
2539                         RemoteClient *client = getClient(peer->id);
2540                         JMutexAutoLock digmutex(client->m_dig_mutex);
2541                         client->m_dig_tool_item = -1;
2542 #endif
2543                 }
2544
2545                 /*
2546                         3: Digging completed
2547                 */
2548                 else if(action == 3)
2549                 {
2550                         // Mandatory parameter; actually used for nothing
2551                         core::map<v3s16, MapBlock*> modified_blocks;
2552
2553                         content_t material = CONTENT_IGNORE;
2554                         u8 mineral = MINERAL_NONE;
2555
2556                         bool cannot_remove_node = false;
2557
2558                         try
2559                         {
2560                                 MapNode n = m_env.getMap().getNode(p_under);
2561                                 // Get mineral
2562                                 mineral = n.getMineral();
2563                                 // Get material at position
2564                                 material = n.getContent();
2565                                 // If not yet cancelled
2566                                 if(cannot_remove_node == false)
2567                                 {
2568                                         // If it's not diggable, do nothing
2569                                         if(content_diggable(material) == false)
2570                                         {
2571                                                 derr_server<<"Server: Not finishing digging: "
2572                                                                 <<"Node not diggable"
2573                                                                 <<std::endl;
2574                                                 cannot_remove_node = true;
2575                                         }
2576                                 }
2577                                 // If not yet cancelled
2578                                 if(cannot_remove_node == false)
2579                                 {
2580                                         // Get node metadata
2581                                         NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2582                                         if(meta && meta->nodeRemovalDisabled() == true)
2583                                         {
2584                                                 derr_server<<"Server: Not finishing digging: "
2585                                                                 <<"Node metadata disables removal"
2586                                                                 <<std::endl;
2587                                                 cannot_remove_node = true;
2588                                         }
2589                                 }
2590                         }
2591                         catch(InvalidPositionException &e)
2592                         {
2593                                 derr_server<<"Server: Not finishing digging: Node not found."
2594                                                 <<" Adding block to emerge queue."
2595                                                 <<std::endl;
2596                                 m_emerge_queue.addBlock(peer_id,
2597                                                 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2598                                 cannot_remove_node = true;
2599                         }
2600
2601                         // Make sure the player is allowed to do it
2602                         if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2603                         {
2604                                 dstream<<"Player "<<player->getName()<<" cannot remove node"
2605                                                 <<" because privileges are "<<getPlayerPrivs(player)
2606                                                 <<std::endl;
2607                                 cannot_remove_node = true;
2608                         }
2609
2610                         /*
2611                                 If node can't be removed, set block to be re-sent to
2612                                 client and quit.
2613                         */
2614                         if(cannot_remove_node)
2615                         {
2616                                 derr_server<<"Server: Not finishing digging."<<std::endl;
2617
2618                                 // Client probably has wrong data.
2619                                 // Set block not sent, so that client will get
2620                                 // a valid one.
2621                                 dstream<<"Client "<<peer_id<<" tried to dig "
2622                                                 <<"node; but node cannot be removed."
2623                                                 <<" setting MapBlock not sent."<<std::endl;
2624                                 RemoteClient *client = getClient(peer_id);
2625                                 v3s16 blockpos = getNodeBlockPos(p_under);
2626                                 client->SetBlockNotSent(blockpos);
2627                                         
2628                                 return;
2629                         }
2630                         
2631                         /*
2632                                 Send the removal to all close-by players.
2633                                 - If other player is close, send REMOVENODE
2634                                 - Otherwise set blocks not sent
2635                         */
2636                         core::list<u16> far_players;
2637                         sendRemoveNode(p_under, peer_id, &far_players, 30);
2638                         
2639                         /*
2640                                 Update and send inventory
2641                         */
2642
2643                         if(g_settings.getBool("creative_mode") == false)
2644                         {
2645                                 /*
2646                                         Wear out tool
2647                                 */
2648                                 InventoryList *mlist = player->inventory.getList("main");
2649                                 if(mlist != NULL)
2650                                 {
2651                                         InventoryItem *item = mlist->getItem(item_i);
2652                                         if(item && (std::string)item->getName() == "ToolItem")
2653                                         {
2654                                                 ToolItem *titem = (ToolItem*)item;
2655                                                 std::string toolname = titem->getToolName();
2656
2657                                                 // Get digging properties for material and tool
2658                                                 DiggingProperties prop =
2659                                                                 getDiggingProperties(material, toolname);
2660
2661                                                 if(prop.diggable == false)
2662                                                 {
2663                                                         derr_server<<"Server: WARNING: Player digged"
2664                                                                         <<" with impossible material + tool"
2665                                                                         <<" combination"<<std::endl;
2666                                                 }
2667                                                 
2668                                                 bool weared_out = titem->addWear(prop.wear);
2669
2670                                                 if(weared_out)
2671                                                 {
2672                                                         mlist->deleteItem(item_i);
2673                                                 }
2674                                         }
2675                                 }
2676
2677                                 /*
2678                                         Add dug item to inventory
2679                                 */
2680
2681                                 InventoryItem *item = NULL;
2682
2683                                 if(mineral != MINERAL_NONE)
2684                                         item = getDiggedMineralItem(mineral);
2685                                 
2686                                 // If not mineral
2687                                 if(item == NULL)
2688                                 {
2689                                         std::string &dug_s = content_features(material).dug_item;
2690                                         if(dug_s != "")
2691                                         {
2692                                                 std::istringstream is(dug_s, std::ios::binary);
2693                                                 item = InventoryItem::deSerialize(is);
2694                                         }
2695                                 }
2696                                 
2697                                 if(item != NULL)
2698                                 {
2699                                         // Add a item to inventory
2700                                         player->inventory.addItem("main", item);
2701
2702                                         // Send inventory
2703                                         UpdateCrafting(player->peer_id);
2704                                         SendInventory(player->peer_id);
2705                                 }
2706                         }
2707
2708                         /*
2709                                 Remove the node
2710                                 (this takes some time so it is done after the quick stuff)
2711                         */
2712                         {
2713                                 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2714
2715                                 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2716                         }
2717                         /*
2718                                 Set blocks not sent to far players
2719                         */
2720                         for(core::list<u16>::Iterator
2721                                         i = far_players.begin();
2722                                         i != far_players.end(); i++)
2723                         {
2724                                 u16 peer_id = *i;
2725                                 RemoteClient *client = getClient(peer_id);
2726                                 if(client==NULL)
2727                                         continue;
2728                                 client->SetBlocksNotSent(modified_blocks);
2729                         }
2730                 }
2731                 
2732                 /*
2733                         1: place block
2734                 */
2735                 else if(action == 1)
2736                 {
2737
2738                         InventoryList *ilist = player->inventory.getList("main");
2739                         if(ilist == NULL)
2740                                 return;
2741
2742                         // Get item
2743                         InventoryItem *item = ilist->getItem(item_i);
2744                         
2745                         // If there is no item, it is not possible to add it anywhere
2746                         if(item == NULL)
2747                                 return;
2748                         
2749                         /*
2750                                 Handle material items
2751                         */
2752                         if(std::string("MaterialItem") == item->getName())
2753                         {
2754                                 try{
2755                                         // Don't add a node if this is not a free space
2756                                         MapNode n2 = m_env.getMap().getNode(p_over);
2757                                         bool no_enough_privs =
2758                                                         ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2759                                         if(no_enough_privs)
2760                                                 dstream<<"Player "<<player->getName()<<" cannot add node"
2761                                                         <<" because privileges are "<<getPlayerPrivs(player)
2762                                                         <<std::endl;
2763
2764                                         if(content_features(n2).buildable_to == false
2765                                                 || no_enough_privs)
2766                                         {
2767                                                 // Client probably has wrong data.
2768                                                 // Set block not sent, so that client will get
2769                                                 // a valid one.
2770                                                 dstream<<"Client "<<peer_id<<" tried to place"
2771                                                                 <<" node in invalid position; setting"
2772                                                                 <<" MapBlock not sent."<<std::endl;
2773                                                 RemoteClient *client = getClient(peer_id);
2774                                                 v3s16 blockpos = getNodeBlockPos(p_over);
2775                                                 client->SetBlockNotSent(blockpos);
2776                                                 return;
2777                                         }
2778                                 }
2779                                 catch(InvalidPositionException &e)
2780                                 {
2781                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2782                                                         <<" Adding block to emerge queue."
2783                                                         <<std::endl;
2784                                         m_emerge_queue.addBlock(peer_id,
2785                                                         getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2786                                         return;
2787                                 }
2788
2789                                 // Reset build time counter
2790                                 getClient(peer->id)->m_time_from_building = 0.0;
2791                                 
2792                                 // Create node data
2793                                 MaterialItem *mitem = (MaterialItem*)item;
2794                                 MapNode n;
2795                                 n.setContent(mitem->getMaterial());
2796
2797                                 // Calculate direction for wall mounted stuff
2798                                 if(content_features(n).wall_mounted)
2799                                         n.param2 = packDir(p_under - p_over);
2800
2801                                 // Calculate the direction for furnaces and chests and stuff
2802                                 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2803                                 {
2804                                         v3f playerpos = player->getPosition();
2805                                         v3f blockpos = intToFloat(p_over, BS) - playerpos;
2806                                         blockpos = blockpos.normalize();
2807                                         n.param1 = 0;
2808                                         if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2809                                                 if (blockpos.X < 0)
2810                                                         n.param1 = 3;
2811                                                 else
2812                                                         n.param1 = 1;
2813                                         } else {
2814                                                 if (blockpos.Z < 0)
2815                                                         n.param1 = 2;
2816                                                 else
2817                                                         n.param1 = 0;
2818                                         }
2819                                 }
2820
2821                                 /*
2822                                         Send to all close-by players
2823                                 */
2824                                 core::list<u16> far_players;
2825                                 sendAddNode(p_over, n, 0, &far_players, 30);
2826                                 
2827                                 /*
2828                                         Handle inventory
2829                                 */
2830                                 InventoryList *ilist = player->inventory.getList("main");
2831                                 if(g_settings.getBool("creative_mode") == false && ilist)
2832                                 {
2833                                         // Remove from inventory and send inventory
2834                                         if(mitem->getCount() == 1)
2835                                                 ilist->deleteItem(item_i);
2836                                         else
2837                                                 mitem->remove(1);
2838                                         // Send inventory
2839                                         UpdateCrafting(peer_id);
2840                                         SendInventory(peer_id);
2841                                 }
2842                                 
2843                                 /*
2844                                         Add node.
2845
2846                                         This takes some time so it is done after the quick stuff
2847                                 */
2848                                 core::map<v3s16, MapBlock*> modified_blocks;
2849                                 {
2850                                         MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2851
2852                                         m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2853                                 }
2854                                 /*
2855                                         Set blocks not sent to far players
2856                                 */
2857                                 for(core::list<u16>::Iterator
2858                                                 i = far_players.begin();
2859                                                 i != far_players.end(); i++)
2860                                 {
2861                                         u16 peer_id = *i;
2862                                         RemoteClient *client = getClient(peer_id);
2863                                         if(client==NULL)
2864                                                 continue;
2865                                         client->SetBlocksNotSent(modified_blocks);
2866                                 }
2867
2868                                 /*
2869                                         Calculate special events
2870                                 */
2871                                 
2872                                 /*if(n.d == CONTENT_MESE)
2873                                 {
2874                                         u32 count = 0;
2875                                         for(s16 z=-1; z<=1; z++)
2876                                         for(s16 y=-1; y<=1; y++)
2877                                         for(s16 x=-1; x<=1; x++)
2878                                         {
2879                                                 
2880                                         }
2881                                 }*/
2882                         }
2883                         /*
2884                                 Place other item (not a block)
2885                         */
2886                         else
2887                         {
2888                                 v3s16 blockpos = getNodeBlockPos(p_over);
2889                                 
2890                                 /*
2891                                         Check that the block is loaded so that the item
2892                                         can properly be added to the static list too
2893                                 */
2894                                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2895                                 if(block==NULL)
2896                                 {
2897                                         derr_server<<"Error while placing object: "
2898                                                         "block not found"<<std::endl;
2899                                         return;
2900                                 }
2901
2902                                 dout_server<<"Placing a miscellaneous item on map"
2903                                                 <<std::endl;
2904                                 
2905                                 // Calculate a position for it
2906                                 v3f pos = intToFloat(p_over, BS);
2907                                 //pos.Y -= BS*0.45;
2908                                 pos.Y -= BS*0.25; // let it drop a bit
2909                                 // Randomize a bit
2910                                 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2911                                 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2912
2913                                 /*
2914                                         Create the object
2915                                 */
2916                                 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2917
2918                                 if(obj == NULL)
2919                                 {
2920                                         derr_server<<"WARNING: item resulted in NULL object, "
2921                                                         <<"not placing onto map"
2922                                                         <<std::endl;
2923                                 }
2924                                 else
2925                                 {
2926                                         // Add the object to the environment
2927                                         m_env.addActiveObject(obj);
2928                                         
2929                                         dout_server<<"Placed object"<<std::endl;
2930
2931                                         if(g_settings.getBool("creative_mode") == false)
2932                                         {
2933                                                 // Delete the right amount of items from the slot
2934                                                 u16 dropcount = item->getDropCount();
2935                                                 
2936                                                 // Delete item if all gone
2937                                                 if(item->getCount() <= dropcount)
2938                                                 {
2939                                                         if(item->getCount() < dropcount)
2940                                                                 dstream<<"WARNING: Server: dropped more items"
2941                                                                                 <<" than the slot contains"<<std::endl;
2942                                                         
2943                                                         InventoryList *ilist = player->inventory.getList("main");
2944                                                         if(ilist)
2945                                                                 // Remove from inventory and send inventory
2946                                                                 ilist->deleteItem(item_i);
2947                                                 }
2948                                                 // Else decrement it
2949                                                 else
2950                                                         item->remove(dropcount);
2951                                                 
2952                                                 // Send inventory
2953                                                 UpdateCrafting(peer_id);
2954                                                 SendInventory(peer_id);
2955                                         }
2956                                 }
2957                         }
2958
2959                 } // action == 1
2960
2961                 /*
2962                         Catch invalid actions
2963                 */
2964                 else
2965                 {
2966                         derr_server<<"WARNING: Server: Invalid action "
2967                                         <<action<<std::endl;
2968                 }
2969         }
2970 #if 0
2971         else if(command == TOSERVER_RELEASE)
2972         {
2973                 if(datasize < 3)
2974                         return;
2975                 /*
2976                         length: 3
2977                         [0] u16 command
2978                         [2] u8 button
2979                 */
2980                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2981         }
2982 #endif
2983         else if(command == TOSERVER_SIGNTEXT)
2984         {
2985                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2986                         return;
2987                 /*
2988                         u16 command
2989                         v3s16 blockpos
2990                         s16 id
2991                         u16 textlen
2992                         textdata
2993                 */
2994                 std::string datastring((char*)&data[2], datasize-2);
2995                 std::istringstream is(datastring, std::ios_base::binary);
2996                 u8 buf[6];
2997                 // Read stuff
2998                 is.read((char*)buf, 6);
2999                 v3s16 blockpos = readV3S16(buf);
3000                 is.read((char*)buf, 2);
3001                 s16 id = readS16(buf);
3002                 is.read((char*)buf, 2);
3003                 u16 textlen = readU16(buf);
3004                 std::string text;
3005                 for(u16 i=0; i<textlen; i++)
3006                 {
3007                         is.read((char*)buf, 1);
3008                         text += (char)buf[0];
3009                 }
3010
3011                 MapBlock *block = NULL;
3012                 try
3013                 {
3014                         block = m_env.getMap().getBlockNoCreate(blockpos);
3015                 }
3016                 catch(InvalidPositionException &e)
3017                 {
3018                         derr_server<<"Error while setting sign text: "
3019                                         "block not found"<<std::endl;
3020                         return;
3021                 }
3022
3023                 MapBlockObject *obj = block->getObject(id);
3024                 if(obj == NULL)
3025                 {
3026                         derr_server<<"Error while setting sign text: "
3027                                         "object not found"<<std::endl;
3028                         return;
3029                 }
3030                 
3031                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3032                 {
3033                         derr_server<<"Error while setting sign text: "
3034                                         "object is not a sign"<<std::endl;
3035                         return;
3036                 }
3037
3038                 ((SignObject*)obj)->setText(text);
3039
3040                 obj->getBlock()->setChangedFlag();
3041         }
3042         else if(command == TOSERVER_SIGNNODETEXT)
3043         {
3044                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3045                         return;
3046                 /*
3047                         u16 command
3048                         v3s16 p
3049                         u16 textlen
3050                         textdata
3051                 */
3052                 std::string datastring((char*)&data[2], datasize-2);
3053                 std::istringstream is(datastring, std::ios_base::binary);
3054                 u8 buf[6];
3055                 // Read stuff
3056                 is.read((char*)buf, 6);
3057                 v3s16 p = readV3S16(buf);
3058                 is.read((char*)buf, 2);
3059                 u16 textlen = readU16(buf);
3060                 std::string text;
3061                 for(u16 i=0; i<textlen; i++)
3062                 {
3063                         is.read((char*)buf, 1);
3064                         text += (char)buf[0];
3065                 }
3066
3067                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3068                 if(!meta)
3069                         return;
3070                 if(meta->typeId() != CONTENT_SIGN_WALL)
3071                         return;
3072                 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3073                 signmeta->setText(text);
3074                 
3075                 v3s16 blockpos = getNodeBlockPos(p);
3076                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3077                 if(block)
3078                 {
3079                         block->setChangedFlag();
3080                 }
3081
3082                 for(core::map<u16, RemoteClient*>::Iterator
3083                         i = m_clients.getIterator();
3084                         i.atEnd()==false; i++)
3085                 {
3086                         RemoteClient *client = i.getNode()->getValue();
3087                         client->SetBlockNotSent(blockpos);
3088                 }
3089         }
3090         else if(command == TOSERVER_INVENTORY_ACTION)
3091         {
3092                 /*// Ignore inventory changes if in creative mode
3093                 if(g_settings.getBool("creative_mode") == true)
3094                 {
3095                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3096                                         <<std::endl;
3097                         return;
3098                 }*/
3099                 // Strip command and create a stream
3100                 std::string datastring((char*)&data[2], datasize-2);
3101                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3102                 std::istringstream is(datastring, std::ios_base::binary);
3103                 // Create an action
3104                 InventoryAction *a = InventoryAction::deSerialize(is);
3105                 if(a != NULL)
3106                 {
3107                         // Create context
3108                         InventoryContext c;
3109                         c.current_player = player;
3110
3111                         /*
3112                                 Handle craftresult specially if not in creative mode
3113                         */
3114                         bool disable_action = false;
3115                         if(a->getType() == IACTION_MOVE
3116                                         && g_settings.getBool("creative_mode") == false)
3117                         {
3118                                 IMoveAction *ma = (IMoveAction*)a;
3119                                 if(ma->to_inv == "current_player" &&
3120                                                 ma->from_inv == "current_player")
3121                                 {
3122                                         InventoryList *rlist = player->inventory.getList("craftresult");
3123                                         assert(rlist);
3124                                         InventoryList *clist = player->inventory.getList("craft");
3125                                         assert(clist);
3126                                         InventoryList *mlist = player->inventory.getList("main");
3127                                         assert(mlist);
3128                                         /*
3129                                                 Craftresult is no longer preview if something
3130                                                 is moved into it
3131                                         */
3132                                         if(ma->to_list == "craftresult"
3133                                                         && ma->from_list != "craftresult")
3134                                         {
3135                                                 // If it currently is a preview, remove
3136                                                 // its contents
3137                                                 if(player->craftresult_is_preview)
3138                                                 {
3139                                                         rlist->deleteItem(0);
3140                                                 }
3141                                                 player->craftresult_is_preview = false;
3142                                         }
3143                                         /*
3144                                                 Crafting takes place if this condition is true.
3145                                         */
3146                                         if(player->craftresult_is_preview &&
3147                                                         ma->from_list == "craftresult")
3148                                         {
3149                                                 player->craftresult_is_preview = false;
3150                                                 clist->decrementMaterials(1);
3151                                         }
3152                                         /*
3153                                                 If the craftresult is placed on itself, move it to
3154                                                 main inventory instead of doing the action
3155                                         */
3156                                         if(ma->to_list == "craftresult"
3157                                                         && ma->from_list == "craftresult")
3158                                         {
3159                                                 disable_action = true;
3160                                                 
3161                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
3162                                                 mlist->addItem(item1);
3163                                         }
3164                                 }
3165                         }
3166                         
3167                         if(disable_action == false)
3168                         {
3169                                 // Feed action to player inventory
3170                                 a->apply(&c, this);
3171                                 // Eat the action
3172                                 delete a;
3173                         }
3174                         else
3175                         {
3176                                 // Send inventory
3177                                 UpdateCrafting(player->peer_id);
3178                                 SendInventory(player->peer_id);
3179                         }
3180                 }
3181                 else
3182                 {
3183                         dstream<<"TOSERVER_INVENTORY_ACTION: "
3184                                         <<"InventoryAction::deSerialize() returned NULL"
3185                                         <<std::endl;
3186                 }
3187         }
3188         else if(command == TOSERVER_CHAT_MESSAGE)
3189         {
3190                 /*
3191                         u16 command
3192                         u16 length
3193                         wstring message
3194                 */
3195                 u8 buf[6];
3196                 std::string datastring((char*)&data[2], datasize-2);
3197                 std::istringstream is(datastring, std::ios_base::binary);
3198                 
3199                 // Read stuff
3200                 is.read((char*)buf, 2);
3201                 u16 len = readU16(buf);
3202                 
3203                 std::wstring message;
3204                 for(u16 i=0; i<len; i++)
3205                 {
3206                         is.read((char*)buf, 2);
3207                         message += (wchar_t)readU16(buf);
3208                 }
3209
3210                 // Get player name of this client
3211                 std::wstring name = narrow_to_wide(player->getName());
3212                 
3213                 // Line to send to players
3214                 std::wstring line;
3215                 // Whether to send to the player that sent the line
3216                 bool send_to_sender = false;
3217                 // Whether to send to other players
3218                 bool send_to_others = false;
3219                 
3220                 // Local player gets all privileges regardless of
3221                 // what's set on their account.
3222                 u64 privs = getPlayerPrivs(player);
3223
3224                 // Parse commands
3225                 std::wstring commandprefix = L"/#";
3226                 if(message.substr(0, commandprefix.size()) == commandprefix)
3227                 {
3228                         line += L"Server: ";
3229
3230                         message = message.substr(commandprefix.size());
3231                         
3232                         WStrfnd f1(message);
3233                         f1.next(L" "); // Skip over /#whatever
3234                         std::wstring paramstring = f1.next(L"");
3235
3236                         ServerCommandContext *ctx = new ServerCommandContext(
3237                                 str_split(message, L' '),
3238                                 paramstring,
3239                                 this,
3240                                 &m_env,
3241                                 player,
3242                                 privs);
3243
3244                         line += processServerCommand(ctx);
3245                         send_to_sender = ctx->flags & 1;
3246                         send_to_others = ctx->flags & 2;
3247                         delete ctx;
3248
3249                 }
3250                 else
3251                 {
3252                         if(privs & PRIV_SHOUT)
3253                         {
3254                                 line += L"<";
3255                                 line += name;
3256                                 line += L"> ";
3257                                 line += message;
3258                                 send_to_others = true;
3259                         }
3260                         else
3261                         {
3262                                 line += L"Server: You are not allowed to shout";
3263                                 send_to_sender = true;
3264                         }
3265                 }
3266                 
3267                 if(line != L"")
3268                 {
3269                         dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3270
3271                         /*
3272                                 Send the message to clients
3273                         */
3274                         for(core::map<u16, RemoteClient*>::Iterator
3275                                 i = m_clients.getIterator();
3276                                 i.atEnd() == false; i++)
3277                         {
3278                                 // Get client and check that it is valid
3279                                 RemoteClient *client = i.getNode()->getValue();
3280                                 assert(client->peer_id == i.getNode()->getKey());
3281                                 if(client->serialization_version == SER_FMT_VER_INVALID)
3282                                         continue;
3283
3284                                 // Filter recipient
3285                                 bool sender_selected = (peer_id == client->peer_id);
3286                                 if(sender_selected == true && send_to_sender == false)
3287                                         continue;
3288                                 if(sender_selected == false && send_to_others == false)
3289                                         continue;
3290
3291                                 SendChatMessage(client->peer_id, line);
3292                         }
3293                 }
3294         }
3295         else if(command == TOSERVER_DAMAGE)
3296         {
3297                 if(g_settings.getBool("enable_damage"))
3298                 {
3299                         std::string datastring((char*)&data[2], datasize-2);
3300                         std::istringstream is(datastring, std::ios_base::binary);
3301                         u8 damage = readU8(is);
3302                         if(player->hp > damage)
3303                         {
3304                                 player->hp -= damage;
3305                         }
3306                         else
3307                         {
3308                                 player->hp = 0;
3309
3310                                 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3311                                                 <<std::endl;
3312                                 
3313                                 v3f pos = findSpawnPos(m_env.getServerMap());
3314                                 player->setPosition(pos);
3315                                 player->hp = 20;
3316                                 SendMovePlayer(player);
3317                                 SendPlayerHP(player);
3318                                 
3319                                 //TODO: Throw items around
3320                         }
3321                 }
3322
3323                 SendPlayerHP(player);
3324         }
3325         else if(command == TOSERVER_PASSWORD)
3326         {
3327                 /*
3328                         [0] u16 TOSERVER_PASSWORD
3329                         [2] u8[28] old password
3330                         [30] u8[28] new password
3331                 */
3332
3333                 if(datasize != 2+PASSWORD_SIZE*2)
3334                         return;
3335                 /*char password[PASSWORD_SIZE];
3336                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3337                         password[i] = data[2+i];
3338                 password[PASSWORD_SIZE-1] = 0;*/
3339                 std::string oldpwd;
3340                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3341                 {
3342                         char c = data[2+i];
3343                         if(c == 0)
3344                                 break;
3345                         oldpwd += c;
3346                 }
3347                 std::string newpwd;
3348                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3349                 {
3350                         char c = data[2+PASSWORD_SIZE+i];
3351                         if(c == 0)
3352                                 break;
3353                         newpwd += c;
3354                 }
3355
3356                 dstream<<"Server: Client requests a password change from "
3357                                 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3358
3359                 std::string playername = player->getName();
3360
3361                 if(m_authmanager.exists(playername) == false)
3362                 {
3363                         dstream<<"Server: playername not found in authmanager"<<std::endl;
3364                         // Wrong old password supplied!!
3365                         SendChatMessage(peer_id, L"playername not found in authmanager");
3366                         return;
3367                 }
3368
3369                 std::string checkpwd = m_authmanager.getPassword(playername);
3370
3371                 if(oldpwd != checkpwd)
3372                 {
3373                         dstream<<"Server: invalid old password"<<std::endl;
3374                         // Wrong old password supplied!!
3375                         SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3376                         return;
3377                 }
3378
3379                 m_authmanager.setPassword(playername, newpwd);
3380                 
3381                 dstream<<"Server: password change successful for "<<playername
3382                                 <<std::endl;
3383                 SendChatMessage(peer_id, L"Password change successful");
3384         }
3385         else
3386         {
3387                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3388                                 "unknown command "<<command<<std::endl;
3389         }
3390         
3391         } //try
3392         catch(SendFailedException &e)
3393         {
3394                 derr_server<<"Server::ProcessData(): SendFailedException: "
3395                                 <<"what="<<e.what()
3396                                 <<std::endl;
3397         }
3398 }
3399
3400 void Server::onMapEditEvent(MapEditEvent *event)
3401 {
3402         //dstream<<"Server::onMapEditEvent()"<<std::endl;
3403         if(m_ignore_map_edit_events)
3404                 return;
3405         MapEditEvent *e = event->clone();
3406         m_unsent_map_edit_queue.push_back(e);
3407 }
3408
3409 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3410 {
3411         if(id == "current_player")
3412         {
3413                 assert(c->current_player);
3414                 return &(c->current_player->inventory);
3415         }
3416         
3417         Strfnd fn(id);
3418         std::string id0 = fn.next(":");
3419
3420         if(id0 == "nodemeta")
3421         {
3422                 v3s16 p;
3423                 p.X = stoi(fn.next(","));
3424                 p.Y = stoi(fn.next(","));
3425                 p.Z = stoi(fn.next(","));
3426                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3427                 if(meta)
3428                         return meta->getInventory();
3429                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3430                                 <<"no metadata found"<<std::endl;
3431                 return NULL;
3432         }
3433
3434         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3435         return NULL;
3436 }
3437 void Server::inventoryModified(InventoryContext *c, std::string id)
3438 {
3439         if(id == "current_player")
3440         {
3441                 assert(c->current_player);
3442                 // Send inventory
3443                 UpdateCrafting(c->current_player->peer_id);
3444                 SendInventory(c->current_player->peer_id);
3445                 return;
3446         }
3447         
3448         Strfnd fn(id);
3449         std::string id0 = fn.next(":");
3450
3451         if(id0 == "nodemeta")
3452         {
3453                 v3s16 p;
3454                 p.X = stoi(fn.next(","));
3455                 p.Y = stoi(fn.next(","));
3456                 p.Z = stoi(fn.next(","));
3457                 v3s16 blockpos = getNodeBlockPos(p);
3458
3459                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3460                 if(meta)
3461                         meta->inventoryModified();
3462
3463                 for(core::map<u16, RemoteClient*>::Iterator
3464                         i = m_clients.getIterator();
3465                         i.atEnd()==false; i++)
3466                 {
3467                         RemoteClient *client = i.getNode()->getValue();
3468                         client->SetBlockNotSent(blockpos);
3469                 }
3470
3471                 return;
3472         }
3473
3474         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3475 }
3476
3477 core::list<PlayerInfo> Server::getPlayerInfo()
3478 {
3479         DSTACK(__FUNCTION_NAME);
3480         JMutexAutoLock envlock(m_env_mutex);
3481         JMutexAutoLock conlock(m_con_mutex);
3482         
3483         core::list<PlayerInfo> list;
3484
3485         core::list<Player*> players = m_env.getPlayers();
3486         
3487         core::list<Player*>::Iterator i;
3488         for(i = players.begin();
3489                         i != players.end(); i++)
3490         {
3491                 PlayerInfo info;
3492
3493                 Player *player = *i;
3494
3495                 try{
3496                         con::Peer *peer = m_con.GetPeer(player->peer_id);
3497                         // Copy info from peer to info struct
3498                         info.id = peer->id;
3499                         info.address = peer->address;
3500                         info.avg_rtt = peer->avg_rtt;
3501                 }
3502                 catch(con::PeerNotFoundException &e)
3503                 {
3504                         // Set dummy peer info
3505                         info.id = 0;
3506                         info.address = Address(0,0,0,0,0);
3507                         info.avg_rtt = 0.0;
3508                 }
3509
3510                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3511                 info.position = player->getPosition();
3512
3513                 list.push_back(info);
3514         }
3515
3516         return list;
3517 }
3518
3519
3520 void Server::peerAdded(con::Peer *peer)
3521 {
3522         DSTACK(__FUNCTION_NAME);
3523         dout_server<<"Server::peerAdded(): peer->id="
3524                         <<peer->id<<std::endl;
3525         
3526         PeerChange c;
3527         c.type = PEER_ADDED;
3528         c.peer_id = peer->id;
3529         c.timeout = false;
3530         m_peer_change_queue.push_back(c);
3531 }
3532
3533 void Server::deletingPeer(con::Peer *peer, bool timeout)
3534 {
3535         DSTACK(__FUNCTION_NAME);
3536         dout_server<<"Server::deletingPeer(): peer->id="
3537                         <<peer->id<<", timeout="<<timeout<<std::endl;
3538         
3539         PeerChange c;
3540         c.type = PEER_REMOVED;
3541         c.peer_id = peer->id;
3542         c.timeout = timeout;
3543         m_peer_change_queue.push_back(c);
3544 }
3545
3546 /*
3547         Static send methods
3548 */
3549
3550 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3551 {
3552         DSTACK(__FUNCTION_NAME);
3553         std::ostringstream os(std::ios_base::binary);
3554
3555         writeU16(os, TOCLIENT_HP);
3556         writeU8(os, hp);
3557
3558         // Make data buffer
3559         std::string s = os.str();
3560         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3561         // Send as reliable
3562         con.Send(peer_id, 0, data, true);
3563 }
3564
3565 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3566                 const std::wstring &reason)
3567 {
3568         DSTACK(__FUNCTION_NAME);
3569         std::ostringstream os(std::ios_base::binary);
3570
3571         writeU16(os, TOCLIENT_ACCESS_DENIED);
3572         os<<serializeWideString(reason);
3573
3574         // Make data buffer
3575         std::string s = os.str();
3576         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3577         // Send as reliable
3578         con.Send(peer_id, 0, data, true);
3579 }
3580
3581 /*
3582         Non-static send methods
3583 */
3584
3585 void Server::SendObjectData(float dtime)
3586 {
3587         DSTACK(__FUNCTION_NAME);
3588
3589         core::map<v3s16, bool> stepped_blocks;
3590         
3591         for(core::map<u16, RemoteClient*>::Iterator
3592                 i = m_clients.getIterator();
3593                 i.atEnd() == false; i++)
3594         {
3595                 u16 peer_id = i.getNode()->getKey();
3596                 RemoteClient *client = i.getNode()->getValue();
3597                 assert(client->peer_id == peer_id);
3598                 
3599                 if(client->serialization_version == SER_FMT_VER_INVALID)
3600                         continue;
3601                 
3602                 client->SendObjectData(this, dtime, stepped_blocks);
3603         }
3604 }
3605
3606 void Server::SendPlayerInfos()
3607 {
3608         DSTACK(__FUNCTION_NAME);
3609
3610         //JMutexAutoLock envlock(m_env_mutex);
3611         
3612         // Get connected players
3613         core::list<Player*> players = m_env.getPlayers(true);
3614         
3615         u32 player_count = players.getSize();
3616         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3617
3618         SharedBuffer<u8> data(datasize);
3619         writeU16(&data[0], TOCLIENT_PLAYERINFO);
3620         
3621         u32 start = 2;
3622         core::list<Player*>::Iterator i;
3623         for(i = players.begin();
3624                         i != players.end(); i++)
3625         {
3626                 Player *player = *i;
3627
3628                 /*dstream<<"Server sending player info for player with "
3629                                 "peer_id="<<player->peer_id<<std::endl;*/
3630                 
3631                 writeU16(&data[start], player->peer_id);
3632                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3633                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3634                 start += 2+PLAYERNAME_SIZE;
3635         }
3636
3637         //JMutexAutoLock conlock(m_con_mutex);
3638
3639         // Send as reliable
3640         m_con.SendToAll(0, data, true);
3641 }
3642
3643 void Server::SendInventory(u16 peer_id)
3644 {
3645         DSTACK(__FUNCTION_NAME);
3646         
3647         Player* player = m_env.getPlayer(peer_id);
3648         assert(player);
3649
3650         /*
3651                 Serialize it
3652         */
3653
3654         std::ostringstream os;
3655         //os.imbue(std::locale("C"));
3656
3657         player->inventory.serialize(os);
3658
3659         std::string s = os.str();
3660         
3661         SharedBuffer<u8> data(s.size()+2);
3662         writeU16(&data[0], TOCLIENT_INVENTORY);
3663         memcpy(&data[2], s.c_str(), s.size());
3664         
3665         // Send as reliable
3666         m_con.Send(peer_id, 0, data, true);
3667 }
3668
3669 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3670 {
3671         DSTACK(__FUNCTION_NAME);
3672         
3673         std::ostringstream os(std::ios_base::binary);
3674         u8 buf[12];
3675         
3676         // Write command
3677         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3678         os.write((char*)buf, 2);
3679         
3680         // Write length
3681         writeU16(buf, message.size());
3682         os.write((char*)buf, 2);
3683         
3684         // Write string
3685         for(u32 i=0; i<message.size(); i++)
3686         {
3687                 u16 w = message[i];
3688                 writeU16(buf, w);
3689                 os.write((char*)buf, 2);
3690         }
3691         
3692         // Make data buffer
3693         std::string s = os.str();
3694         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3695         // Send as reliable
3696         m_con.Send(peer_id, 0, data, true);
3697 }
3698
3699 void Server::BroadcastChatMessage(const std::wstring &message)
3700 {
3701         for(core::map<u16, RemoteClient*>::Iterator
3702                 i = m_clients.getIterator();
3703                 i.atEnd() == false; i++)
3704         {
3705                 // Get client and check that it is valid
3706                 RemoteClient *client = i.getNode()->getValue();
3707                 assert(client->peer_id == i.getNode()->getKey());
3708                 if(client->serialization_version == SER_FMT_VER_INVALID)
3709                         continue;
3710
3711                 SendChatMessage(client->peer_id, message);
3712         }
3713 }
3714
3715 void Server::SendPlayerHP(Player *player)
3716 {
3717         SendHP(m_con, player->peer_id, player->hp);
3718 }
3719
3720 void Server::SendMovePlayer(Player *player)
3721 {
3722         DSTACK(__FUNCTION_NAME);
3723         std::ostringstream os(std::ios_base::binary);
3724
3725         writeU16(os, TOCLIENT_MOVE_PLAYER);
3726         writeV3F1000(os, player->getPosition());
3727         writeF1000(os, player->getPitch());
3728         writeF1000(os, player->getYaw());
3729         
3730         {
3731                 v3f pos = player->getPosition();
3732                 f32 pitch = player->getPitch();
3733                 f32 yaw = player->getYaw();
3734                 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3735                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3736                                 <<" pitch="<<pitch
3737                                 <<" yaw="<<yaw
3738                                 <<std::endl;
3739         }
3740
3741         // Make data buffer
3742         std::string s = os.str();
3743         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3744         // Send as reliable
3745         m_con.Send(player->peer_id, 0, data, true);
3746 }
3747
3748 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3749         core::list<u16> *far_players, float far_d_nodes)
3750 {
3751         float maxd = far_d_nodes*BS;
3752         v3f p_f = intToFloat(p, BS);
3753
3754         // Create packet
3755         u32 replysize = 8;
3756         SharedBuffer<u8> reply(replysize);
3757         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3758         writeS16(&reply[2], p.X);
3759         writeS16(&reply[4], p.Y);
3760         writeS16(&reply[6], p.Z);
3761
3762         for(core::map<u16, RemoteClient*>::Iterator
3763                 i = m_clients.getIterator();
3764                 i.atEnd() == false; i++)
3765         {
3766                 // Get client and check that it is valid
3767                 RemoteClient *client = i.getNode()->getValue();
3768                 assert(client->peer_id == i.getNode()->getKey());
3769                 if(client->serialization_version == SER_FMT_VER_INVALID)
3770                         continue;
3771
3772                 // Don't send if it's the same one
3773                 if(client->peer_id == ignore_id)
3774                         continue;
3775                 
3776                 if(far_players)
3777                 {
3778                         // Get player
3779                         Player *player = m_env.getPlayer(client->peer_id);
3780                         if(player)
3781                         {
3782                                 // If player is far away, only set modified blocks not sent
3783                                 v3f player_pos = player->getPosition();
3784                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3785                                 {
3786                                         far_players->push_back(client->peer_id);
3787                                         continue;
3788                                 }
3789                         }
3790                 }
3791
3792                 // Send as reliable
3793                 m_con.Send(client->peer_id, 0, reply, true);
3794         }
3795 }
3796
3797 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3798                 core::list<u16> *far_players, float far_d_nodes)
3799 {
3800         float maxd = far_d_nodes*BS;
3801         v3f p_f = intToFloat(p, BS);
3802
3803         for(core::map<u16, RemoteClient*>::Iterator
3804                 i = m_clients.getIterator();
3805                 i.atEnd() == false; i++)
3806         {
3807                 // Get client and check that it is valid
3808                 RemoteClient *client = i.getNode()->getValue();
3809                 assert(client->peer_id == i.getNode()->getKey());
3810                 if(client->serialization_version == SER_FMT_VER_INVALID)
3811                         continue;
3812
3813                 // Don't send if it's the same one
3814                 if(client->peer_id == ignore_id)
3815                         continue;
3816
3817                 if(far_players)
3818                 {
3819                         // Get player
3820                         Player *player = m_env.getPlayer(client->peer_id);
3821                         if(player)
3822                         {
3823                                 // If player is far away, only set modified blocks not sent
3824                                 v3f player_pos = player->getPosition();
3825                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3826                                 {
3827                                         far_players->push_back(client->peer_id);
3828                                         continue;
3829                                 }
3830                         }
3831                 }
3832
3833                 // Create packet
3834                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3835                 SharedBuffer<u8> reply(replysize);
3836                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3837                 writeS16(&reply[2], p.X);
3838                 writeS16(&reply[4], p.Y);
3839                 writeS16(&reply[6], p.Z);
3840                 n.serialize(&reply[8], client->serialization_version);
3841
3842                 // Send as reliable
3843                 m_con.Send(client->peer_id, 0, reply, true);
3844         }
3845 }
3846
3847 void Server::setBlockNotSent(v3s16 p)
3848 {
3849         for(core::map<u16, RemoteClient*>::Iterator
3850                 i = m_clients.getIterator();
3851                 i.atEnd()==false; i++)
3852         {
3853                 RemoteClient *client = i.getNode()->getValue();
3854                 client->SetBlockNotSent(p);
3855         }
3856 }
3857
3858 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3859 {
3860         DSTACK(__FUNCTION_NAME);
3861
3862         v3s16 p = block->getPos();
3863         
3864 #if 0
3865         // Analyze it a bit
3866         bool completely_air = true;
3867         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3868         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3869         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3870         {
3871                 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3872                 {
3873                         completely_air = false;
3874                         x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3875                 }
3876         }
3877
3878         // Print result
3879         dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3880         if(completely_air)
3881                 dstream<<"[completely air] ";
3882         dstream<<std::endl;
3883 #endif
3884
3885         /*
3886                 Create a packet with the block in the right format
3887         */
3888         
3889         std::ostringstream os(std::ios_base::binary);
3890         block->serialize(os, ver);
3891         std::string s = os.str();
3892         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3893
3894         u32 replysize = 8 + blockdata.getSize();
3895         SharedBuffer<u8> reply(replysize);
3896         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3897         writeS16(&reply[2], p.X);
3898         writeS16(&reply[4], p.Y);
3899         writeS16(&reply[6], p.Z);
3900         memcpy(&reply[8], *blockdata, blockdata.getSize());
3901
3902         /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3903                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3904         
3905         /*
3906                 Send packet
3907         */
3908         m_con.Send(peer_id, 1, reply, true);
3909 }
3910
3911 void Server::SendBlocks(float dtime)
3912 {
3913         DSTACK(__FUNCTION_NAME);
3914
3915         JMutexAutoLock envlock(m_env_mutex);
3916         JMutexAutoLock conlock(m_con_mutex);
3917
3918         //TimeTaker timer("Server::SendBlocks");
3919
3920         core::array<PrioritySortedBlockTransfer> queue;
3921
3922         s32 total_sending = 0;
3923         
3924         {
3925                 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3926
3927                 for(core::map<u16, RemoteClient*>::Iterator
3928                         i = m_clients.getIterator();
3929                         i.atEnd() == false; i++)
3930                 {
3931                         RemoteClient *client = i.getNode()->getValue();
3932                         assert(client->peer_id == i.getNode()->getKey());
3933
3934                         total_sending += client->SendingCount();
3935                         
3936                         if(client->serialization_version == SER_FMT_VER_INVALID)
3937                                 continue;
3938                         
3939                         client->GetNextBlocks(this, dtime, queue);
3940                 }
3941         }
3942
3943         // Sort.
3944         // Lowest priority number comes first.
3945         // Lowest is most important.
3946         queue.sort();
3947
3948         for(u32 i=0; i<queue.size(); i++)
3949         {
3950                 //TODO: Calculate limit dynamically
3951                 if(total_sending >= g_settings.getS32
3952                                 ("max_simultaneous_block_sends_server_total"))
3953                         break;
3954                 
3955                 PrioritySortedBlockTransfer q = queue[i];
3956
3957                 MapBlock *block = NULL;
3958                 try
3959                 {
3960                         block = m_env.getMap().getBlockNoCreate(q.pos);
3961                 }
3962                 catch(InvalidPositionException &e)
3963                 {
3964                         continue;
3965                 }
3966
3967                 RemoteClient *client = getClient(q.peer_id);
3968
3969                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3970
3971                 client->SentBlock(q.pos);
3972
3973                 total_sending++;
3974         }
3975 }
3976
3977 /*
3978         Something random
3979 */
3980
3981 void Server::UpdateCrafting(u16 peer_id)
3982 {
3983         DSTACK(__FUNCTION_NAME);
3984         
3985         Player* player = m_env.getPlayer(peer_id);
3986         assert(player);
3987
3988         /*
3989                 Calculate crafting stuff
3990         */
3991         if(g_settings.getBool("creative_mode") == false)
3992         {
3993                 InventoryList *clist = player->inventory.getList("craft");
3994                 InventoryList *rlist = player->inventory.getList("craftresult");
3995
3996                 if(rlist->getUsedSlots() == 0)
3997                         player->craftresult_is_preview = true;
3998
3999                 if(rlist && player->craftresult_is_preview)
4000                 {
4001                         rlist->clearItems();
4002                 }
4003                 if(clist && rlist && player->craftresult_is_preview)
4004                 {
4005                         InventoryItem *items[9];
4006                         for(u16 i=0; i<9; i++)
4007                         {
4008                                 items[i] = clist->getItem(i);
4009                         }
4010                         
4011                         // Get result of crafting grid
4012                         InventoryItem *result = craft_get_result(items);
4013                         if(result)
4014                                 rlist->addItem(result);
4015                 }
4016         
4017         } // if creative_mode == false
4018 }
4019
4020 RemoteClient* Server::getClient(u16 peer_id)
4021 {
4022         DSTACK(__FUNCTION_NAME);
4023         //JMutexAutoLock lock(m_con_mutex);
4024         core::map<u16, RemoteClient*>::Node *n;
4025         n = m_clients.find(peer_id);
4026         // A client should exist for all peers
4027         assert(n != NULL);
4028         return n->getValue();
4029 }
4030
4031 std::wstring Server::getStatusString()
4032 {
4033         std::wostringstream os(std::ios_base::binary);
4034         os<<L"# Server: ";
4035         // Version
4036         os<<L"version="<<narrow_to_wide(VERSION_STRING);
4037         // Uptime
4038         os<<L", uptime="<<m_uptime.get();
4039         // Information about clients
4040         os<<L", clients={";
4041         for(core::map<u16, RemoteClient*>::Iterator
4042                 i = m_clients.getIterator();
4043                 i.atEnd() == false; i++)
4044         {
4045                 // Get client and check that it is valid
4046                 RemoteClient *client = i.getNode()->getValue();
4047                 assert(client->peer_id == i.getNode()->getKey());
4048                 if(client->serialization_version == SER_FMT_VER_INVALID)
4049                         continue;
4050                 // Get player
4051                 Player *player = m_env.getPlayer(client->peer_id);
4052                 // Get name of player
4053                 std::wstring name = L"unknown";
4054                 if(player != NULL)
4055                         name = narrow_to_wide(player->getName());
4056                 // Add name to information string
4057                 os<<name<<L",";
4058         }
4059         os<<L"}";
4060         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4061                 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4062         if(g_settings.get("motd") != "")
4063                 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
4064         return os.str();
4065 }
4066
4067 v3f findSpawnPos(ServerMap &map)
4068 {
4069         //return v3f(50,50,50)*BS;
4070
4071         v2s16 nodepos;
4072         s16 groundheight = 0;
4073         
4074 #if 0
4075         nodepos = v2s16(0,0);
4076         groundheight = 20;
4077 #endif
4078
4079 #if 1
4080         // Try to find a good place a few times
4081         for(s32 i=0; i<1000; i++)
4082         {
4083                 s32 range = 1 + i;
4084                 // We're going to try to throw the player to this position
4085                 nodepos = v2s16(-range + (myrand()%(range*2)),
4086                                 -range + (myrand()%(range*2)));
4087                 v2s16 sectorpos = getNodeSectorPos(nodepos);
4088                 // Get sector (NOTE: Don't get because it's slow)
4089                 //m_env.getMap().emergeSector(sectorpos);
4090                 // Get ground height at point (fallbacks to heightmap function)
4091                 groundheight = map.findGroundLevel(nodepos);
4092                 // Don't go underwater
4093                 if(groundheight < WATER_LEVEL)
4094                 {
4095                         //dstream<<"-> Underwater"<<std::endl;
4096                         continue;
4097                 }
4098                 // Don't go to high places
4099                 if(groundheight > WATER_LEVEL + 4)
4100                 {
4101                         //dstream<<"-> Underwater"<<std::endl;
4102                         continue;
4103                 }
4104
4105                 // Found a good place
4106                 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4107                 break;
4108         }
4109 #endif
4110         
4111         // If no suitable place was not found, go above water at least.
4112         if(groundheight < WATER_LEVEL)
4113                 groundheight = WATER_LEVEL;
4114
4115         return intToFloat(v3s16(
4116                         nodepos.X,
4117                         groundheight + 3,
4118                         nodepos.Y
4119                         ), BS);
4120 }
4121
4122 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4123 {
4124         /*
4125                 Try to get an existing player
4126         */
4127         Player *player = m_env.getPlayer(name);
4128         if(player != NULL)
4129         {
4130                 // If player is already connected, cancel
4131                 if(player->peer_id != 0)
4132                 {
4133                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
4134                         return NULL;
4135                 }
4136
4137                 // Got one.
4138                 player->peer_id = peer_id;
4139                 
4140                 // Reset inventory to creative if in creative mode
4141                 if(g_settings.getBool("creative_mode"))
4142                 {
4143                         // Warning: double code below
4144                         // Backup actual inventory
4145                         player->inventory_backup = new Inventory();
4146                         *(player->inventory_backup) = player->inventory;
4147                         // Set creative inventory
4148                         craft_set_creative_inventory(player);
4149                 }
4150
4151                 return player;
4152         }
4153
4154         /*
4155                 If player with the wanted peer_id already exists, cancel.
4156         */
4157         if(m_env.getPlayer(peer_id) != NULL)
4158         {
4159                 dstream<<"emergePlayer(): Player with wrong name but same"
4160                                 " peer_id already exists"<<std::endl;
4161                 return NULL;
4162         }
4163         
4164         /*
4165                 Create a new player
4166         */
4167         {
4168                 player = new ServerRemotePlayer();
4169                 //player->peer_id = c.peer_id;
4170                 //player->peer_id = PEER_ID_INEXISTENT;
4171                 player->peer_id = peer_id;
4172                 player->updateName(name);
4173                 m_authmanager.add(name);
4174                 m_authmanager.setPassword(name, password);
4175                 m_authmanager.setPrivs(name,
4176                                 stringToPrivs(g_settings.get("default_privs")));
4177
4178                 /*
4179                         Set player position
4180                 */
4181                 
4182                 dstream<<"Server: Finding spawn place for player \""
4183                                 <<player->getName()<<"\""<<std::endl;
4184
4185                 v3f pos = findSpawnPos(m_env.getServerMap());
4186
4187                 player->setPosition(pos);
4188
4189                 /*
4190                         Add player to environment
4191                 */
4192
4193                 m_env.addPlayer(player);
4194
4195                 /*
4196                         Add stuff to inventory
4197                 */
4198                 
4199                 if(g_settings.getBool("creative_mode"))
4200                 {
4201                         // Warning: double code above
4202                         // Backup actual inventory
4203                         player->inventory_backup = new Inventory();
4204                         *(player->inventory_backup) = player->inventory;
4205                         // Set creative inventory
4206                         craft_set_creative_inventory(player);
4207                 }
4208                 else if(g_settings.getBool("give_initial_stuff"))
4209                 {
4210                         craft_give_initial_stuff(player);
4211                 }
4212
4213                 return player;
4214                 
4215         } // create new player
4216 }
4217
4218 void Server::handlePeerChange(PeerChange &c)
4219 {
4220         JMutexAutoLock envlock(m_env_mutex);
4221         JMutexAutoLock conlock(m_con_mutex);
4222         
4223         if(c.type == PEER_ADDED)
4224         {
4225                 /*
4226                         Add
4227                 */
4228
4229                 // Error check
4230                 core::map<u16, RemoteClient*>::Node *n;
4231                 n = m_clients.find(c.peer_id);
4232                 // The client shouldn't already exist
4233                 assert(n == NULL);
4234
4235                 // Create client
4236                 RemoteClient *client = new RemoteClient();
4237                 client->peer_id = c.peer_id;
4238                 m_clients.insert(client->peer_id, client);
4239
4240         } // PEER_ADDED
4241         else if(c.type == PEER_REMOVED)
4242         {
4243                 /*
4244                         Delete
4245                 */
4246
4247                 // Error check
4248                 core::map<u16, RemoteClient*>::Node *n;
4249                 n = m_clients.find(c.peer_id);
4250                 // The client should exist
4251                 assert(n != NULL);
4252                 
4253                 /*
4254                         Mark objects to be not known by the client
4255                 */
4256                 RemoteClient *client = n->getValue();
4257                 // Handle objects
4258                 for(core::map<u16, bool>::Iterator
4259                                 i = client->m_known_objects.getIterator();
4260                                 i.atEnd()==false; i++)
4261                 {
4262                         // Get object
4263                         u16 id = i.getNode()->getKey();
4264                         ServerActiveObject* obj = m_env.getActiveObject(id);
4265                         
4266                         if(obj && obj->m_known_by_count > 0)
4267                                 obj->m_known_by_count--;
4268                 }
4269
4270                 // Collect information about leaving in chat
4271                 std::wstring message;
4272                 {
4273                         std::wstring name = L"unknown";
4274                         Player *player = m_env.getPlayer(c.peer_id);
4275                         if(player != NULL)
4276                                 name = narrow_to_wide(player->getName());
4277                         
4278                         message += L"*** ";
4279                         message += name;
4280                         message += L" left game";
4281                         if(c.timeout)
4282                                 message += L" (timed out)";
4283                 }
4284
4285                 /*// Delete player
4286                 {
4287                         m_env.removePlayer(c.peer_id);
4288                 }*/
4289
4290                 // Set player client disconnected
4291                 {
4292                         Player *player = m_env.getPlayer(c.peer_id);
4293                         if(player != NULL)
4294                                 player->peer_id = 0;
4295                 }
4296                 
4297                 // Delete client
4298                 delete m_clients[c.peer_id];
4299                 m_clients.remove(c.peer_id);
4300
4301                 // Send player info to all remaining clients
4302                 SendPlayerInfos();
4303                 
4304                 // Send leave chat message to all remaining clients
4305                 BroadcastChatMessage(message);
4306                 
4307         } // PEER_REMOVED
4308         else
4309         {
4310                 assert(0);
4311         }
4312 }
4313
4314 void Server::handlePeerChanges()
4315 {
4316         while(m_peer_change_queue.size() > 0)
4317         {
4318                 PeerChange c = m_peer_change_queue.pop_front();
4319
4320                 dout_server<<"Server: Handling peer change: "
4321                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4322                                 <<std::endl;
4323
4324                 handlePeerChange(c);
4325         }
4326 }
4327
4328 u64 Server::getPlayerPrivs(Player *player)
4329 {
4330         if(player==NULL)
4331                 return 0;
4332         std::string playername = player->getName();
4333         // Local player gets all privileges regardless of
4334         // what's set on their account.
4335         if(g_settings.get("name") == playername)
4336         {
4337                 return PRIV_ALL;
4338         }
4339         else
4340         {
4341                 return getPlayerAuthPrivs(playername);
4342         }
4343 }
4344
4345 void dedicated_server_loop(Server &server, bool &kill)
4346 {
4347         DSTACK(__FUNCTION_NAME);
4348         
4349         dstream<<DTIME<<std::endl;
4350         dstream<<"========================"<<std::endl;
4351         dstream<<"Running dedicated server"<<std::endl;
4352         dstream<<"========================"<<std::endl;
4353         dstream<<std::endl;
4354
4355         IntervalLimiter m_profiler_interval;
4356
4357         for(;;)
4358         {
4359                 // This is kind of a hack but can be done like this
4360                 // because server.step() is very light
4361                 {
4362                         ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4363                         sleep_ms(30);
4364                 }
4365                 server.step(0.030);
4366
4367                 if(server.getShutdownRequested() || kill)
4368                 {
4369                         dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4370                         break;
4371                 }
4372
4373                 /*
4374                         Profiler
4375                 */
4376                 float profiler_print_interval =
4377                                 g_settings.getFloat("profiler_print_interval");
4378                 if(profiler_print_interval != 0)
4379                 {
4380                         if(m_profiler_interval.step(0.030, profiler_print_interval))
4381                         {
4382                                 dstream<<"Profiler:"<<std::endl;
4383                                 g_profiler.print(dstream);
4384                                 g_profiler.clear();
4385                         }
4386                 }
4387                 
4388                 /*
4389                         Player info
4390                 */
4391                 static int counter = 0;
4392                 counter--;
4393                 if(counter <= 0)
4394                 {
4395                         counter = 10;
4396
4397                         core::list<PlayerInfo> list = server.getPlayerInfo();
4398                         core::list<PlayerInfo>::Iterator i;
4399                         static u32 sum_old = 0;
4400                         u32 sum = PIChecksum(list);
4401                         if(sum != sum_old)
4402                         {
4403                                 dstream<<DTIME<<"Player info:"<<std::endl;
4404                                 for(i=list.begin(); i!=list.end(); i++)
4405                                 {
4406                                         i->PrintLine(&dstream);
4407                                 }
4408                         }
4409                         sum_old = sum;
4410                 }
4411         }
4412 }
4413
4414