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